9am/ 775 0 0 0 11334032360 73455ustar00nemosys9am/9CHANGES 664 0 0 72207 11333022600 10454ustar00nemosysThese are changes to the kernel for acpi as of Fri Feb 5 09:04:46 EST 2010 files=(\ /sys/src/9/pc/apic.c \ /sys/src/9/pc/archmp.c \ /sys/src/9/pc/devarch.c \ /sys/src/9/pc/main.c \ /sys/src/9/pc/mkfile \ /sys/src/9/pc/mp.c \ /sys/src/9/pc/mp.h \ /sys/src/9/pc/pcf \ /sys/src/9/port/chan.c \ /sys/src/9/port/initcode.c \ /sys/src/9/port/mkdevc \ /sys/src/9/port/mkrootc \ /sys/src/9/port/portdat.h \ /sys/src/9/port/portfns.h \ ) for(f in $files) diff -n $f /n/src/sources/plan9/$f # 9/pc: # apic.c: clock print; not panic # archmp.c: use the now shared code from mp.c and do not assume # apics have unique ids no matter their kind # devarch.c: prepare to initialize coherency before the rest of arch # main.c: initialization sequence changed to start acpi early and # run 9am before booting # mkfile: new dependencies for *mp* *acpi* *apic* # mp.c: changed to share code for arch[mp,acpi].c and to fix # apic ids problem. # mp.h: change to Apic struct and changes in externs # pcf: new acpi devp9am and /boot/9am # # new files acpi.[ch] devp9am.c # 9/port: # chan.c: chandevreset(int) chandevinit(int) to init boot devices or # everything else. # initcode.c: start 9am before trying boot; it will exec boot later passing # its own args. # mkdevc: put boot devices (before 9am) early in devtab and set bootdevtabs # mkrootc: permit to call bootlinks() more than once (we may do now). # portdat.h: new bootdevtabs var used by chandevreset/init # portfns: changed interface for chandevreset/init; realloc added Diffs as of today wrt /n/sources [some changes shown are changes in sources and not changes made by me]. /sys/src/9/pc/apic.c:155 c /n/src/sources/plan9//sys/src/9/pc/apic.c:155 < iprint("lapic clock %lld > cpu clock > %lld\n", --- > panic("lapic clock %lld > cpu clock > %lld\n", /sys/src/9/pc/archmp.c:10 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:10 < static _MP_ *_mp_; --- > _MP_ *_mp_; /sys/src/9/pc/archmp.c:12,14 d /n/src/sources/plan9//sys/src/9/pc/archmp.c:11 < static PCMP* mppcmp; < static int mpmachno = 1; < /sys/src/9/pc/archmp.c:60 d /n/src/sources/plan9//sys/src/9/pc/archmp.c:56 < static void mpinit(void); /sys/src/9/pc/archmp.c:113,133 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:109 < static char* buses[] = { < "CBUSI ", < "CBUSII", < "EISA ", < "FUTURE", < "INTERN", < "ISA ", < "MBI ", < "MBII ", < "MCA ", < "MPI ", < "MPSA ", < "NUBUS ", < "PCI ", < "PCMCIA", < "TC ", < "VL ", < "VME ", < "XPRESS", < 0, < }; --- > Lock mpsynclock; /sys/src/9/pc/archmp.c:135,136 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:111,112 < static Apic* < mkprocessor(PCMPprocessor* p) --- > void > syncclock(void) /sys/src/9/pc/archmp.c:138 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:114 < Apic *apic; --- > uvlong x; /sys/src/9/pc/archmp.c:140,141 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:116,117 < if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) < return nil; --- > if(arch->fastclock != tscticks) > return; /sys/src/9/pc/archmp.c:143,150 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:119,127 < apic = mpnewapic(PcmpPROCESSOR, p->apicno, p->flags); < apic->lintr[0] = ApicIMASK; < apic->lintr[1] = ApicIMASK; < if(p->flags & PcmpBP) < apic->machno = 0; < else{ < apic->machno = mpmachno; < mpmachno++; --- > if(m->machno == 0){ > wrmsr(0x10, 0); > m->tscticks = 0; > } else { > x = MACHP(0)->tscticks; > while(x == MACHP(0)->tscticks) > ; > wrmsr(0x10, MACHP(0)->tscticks); > cycles(&m->tscticks); /sys/src/9/pc/archmp.c:152,153 d /n/src/sources/plan9//sys/src/9/pc/archmp.c:128 < < return apic; /sys/src/9/pc/archmp.c:156,157 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:131,132 < static Bus* < mkbus(PCMPbus* p) --- > uvlong > tscticks(uvlong *hz) /sys/src/9/pc/archmp.c:159 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:134,135 < int i; --- > if(hz != nil) > *hz = m->cpuhz; /sys/src/9/pc/archmp.c:161,298 c /n/src/sources/plan9//sys/src/9/pc/archmp.c:137,138 < for(i = 0; buses[i]; i++) < if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) < return mpnewbus(i, p->busno); < return nil; < } < < static Apic* < mkioapic(PCMPioapic* p) < { < Apic *apic; < void *va; < < if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) < return nil; < < /* < * Map the I/O APIC. < */ < if((va = vmap(p->addr, 1024)) == nil) < return 0; < < apic = mpnewapic(PcmpIOAPIC, p->apicno, p->flags); < apic->addr = va; < apic->paddr = p->addr; < < return apic; < } < < static void < fixx38mlstintr(Aintr *aintr) < { < PCMPintr *p; < < p = aintr->intr; < /* < * Hack for Intel SR1520ML motherboard, which BIOS describes < * the i82575 dual ethernet controllers incorrectly. < */ < if(memcmp(mppcmp->product, "INTEL X38MLST ", 20) == 0) < if(p->busno == 1 && p->intin == 16 && p->irq == 1){ < print("mkiointr: %20.20s bus %d intin %d irq %d\n", < (char*)mppcmp->product, < p->busno, p->intin, < p->irq); < p->intin = 17; < } < } < < static void < mpinit(void) < { < PCMP *pcmp; < uchar *e, *p; < Apic *apic, *bpapic; < void *va; < Aintr *aintr; < < i8259init(); < syncclock(); < < if(_mp_ == 0) < return; < pcmp = KADDR(_mp_->physaddr); < < /* < * Map the local APIC. < */ < if((va = vmap(pcmp->lapicbase, 1024)) == nil) < return; < mppcmp = pcmp; < print("LAPIC: %.8lux %.8lux\n", pcmp->lapicbase, (ulong)va); < < bpapic = nil; < < /* < * Run through the table saving information needed for starting < * application processors and initialising any I/O APICs. The table < * is guaranteed to be in order such that only one pass is necessary. < */ < p = ((uchar*)pcmp)+sizeof(PCMP); < e = ((uchar*)pcmp)+pcmp->length; < while(p < e) switch(*p){ < < default: < print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", < *p, e-p); < while(p < e){ < print("%uX ", *p); < p++; < } < break; < < case PcmpPROCESSOR: < if(apic = mkprocessor((PCMPprocessor*)p)){ < /* < * Must take a note of bootstrap processor APIC < * now as it will be needed in order to start the < * application processors later and there's no < * guarantee that the bootstrap processor appears < * first in the table before the others. < */ < apic->addr = va; < apic->paddr = pcmp->lapicbase; < if(apic->flags & PcmpBP) < bpapic = apic; < } < p += sizeof(PCMPprocessor); < continue; < < case PcmpBUS: < mkbus((PCMPbus*)p); < p += sizeof(PCMPbus); < continue; < < case PcmpIOAPIC: < if(apic = mkioapic((PCMPioapic*)p)) < ioapicinit(apic, ((PCMPioapic*)p)->apicno); < p += sizeof(PCMPioapic); < continue; < < case PcmpIOINTR: < aintr = mpnewintr((PCMPintr*)p); < fixx38mlstintr(aintr); < p += sizeof(PCMPintr); < continue; < < case PcmpLINTR: < mpnewlintr((PCMPintr*)p); < p += sizeof(PCMPintr); < continue; < } < < /* < * No bootstrap processor, no need to go further. < */ < if(bpapic == 0) < return; < mpstart(bpapic); --- > cycles(&m->tscticks); /* Uses the rdtsc instruction */ > return m->tscticks; /sys/src/9/pc/devarch.c:965,986 d /n/src/sources/plan9//sys/src/9/pc/devarch.c:964 < coherencyinit(void) < { < /* < * Decide whether to use copy-on-reference (386 and mp). < * We get another chance to set it in mpinit() for a < * multiprocessor. < */ < if(X86FAMILY(m->cpuidax) == 3) < conf.copymode = 1; < < if(X86FAMILY(m->cpuidax) >= 4) < cmpswap = cmpswap486; < < if(X86FAMILY(m->cpuidax) >= 5) < coherence = mb586; < < if(m->cpuiddx & Sse2) < coherence = mfence; < < } < < void /sys/src/9/pc/devarch.c:1015 c /n/src/sources/plan9//sys/src/9/pc/devarch.c:993,1008 < coherencyinit(); --- > /* > * Decide whether to use copy-on-reference (386 and mp). > * We get another chance to set it in mpinit() for a > * multiprocessor. > */ > if(X86FAMILY(m->cpuidax) == 3) > conf.copymode = 1; > > if(X86FAMILY(m->cpuidax) >= 4) > cmpswap = cmpswap486; > > if(X86FAMILY(m->cpuidax) >= 5) > coherence = mb586; > > if(m->cpuiddx & Sse2) > coherence = mfence; /sys/src/9/pc/main.c:75 d /n/src/sources/plan9//sys/src/9/pc/main.c:74 < extern void acpiinit(void); /sys/src/9/pc/main.c:105 d /n/src/sources/plan9//sys/src/9/pc/main.c:103 < acpiinit(); /sys/src/9/pc/main.c:115 c /n/src/sources/plan9//sys/src/9/pc/main.c:113,119 < chandevreset(0); --- > if(delaylink){ > bootlinks(); > pcimatch(0, 0, 0); > }else > links(); > conf.monitor = 1; > chandevreset(); /sys/src/9/pc/main.c:116 a /n/src/sources/plan9//sys/src/9/pc/main.c:121 > i8253link(); /sys/src/9/pc/main.c:118 d /n/src/sources/plan9//sys/src/9/pc/main.c:122 < bootlinks(); /sys/src/9/pc/main.c:125,142 d /n/src/sources/plan9//sys/src/9/pc/main.c:128 < startdevs(void) < { < static int done; < < if(done++ > 0){ < print("startdevs called more than once\n"); < return; < } < if(0)print("startdevs\n"); < links(); < conf.monitor = 1; < chandevreset(1); < i8253link(); < chandevinit(1); < kproc("alarm", alarmkproc, 0); < } < < void /sys/src/9/pc/main.c:186 d /n/src/sources/plan9//sys/src/9/pc/main.c:171 < char *p; /sys/src/9/pc/main.c:200 c /n/src/sources/plan9//sys/src/9/pc/main.c:185,187 < chandevinit(0); --- > > chandevinit(); > /sys/src/9/pc/main.c:216,218 c /n/src/sources/plan9//sys/src/9/pc/main.c:203 < if((p = getconf("*9am")) == nil || strtol(p, 0, 0) == 0) < startdevs(); < spllo(); --- > kproc("alarm", alarmkproc, 0); /sys/src/9/pc/mkfile:2 c /n/src/sources/plan9//sys/src/9/pc/mkfile:2 < CONFLIST=pc pccpu pcf pccpuf pcdisk pcauth pcmpf --- > CONFLIST=pc pccpu pcf pccpuf pcdisk pcauth /sys/src/9/pc/mkfile:108,110 c /n/src/sources/plan9//sys/src/9/pc/mkfile:108,109 < mp.$O: mp.h acpi.h apbootstrap.h < archmp.$O apic.$O: mp.h < devp9am.$O acpi.$O archacpi.$O: mp.h acpi.h --- > archmp.$O mp.$O: apbootstrap.h > apic.$O archmp.$O mp.$O: mp.h /sys/src/9/pc/mkfile:122 d /n/src/sources/plan9//sys/src/9/pc/mkfile:120 < devp9am.$O: acpi.h /sys/src/9/pc/mp.c:10 d /n/src/sources/plan9//sys/src/9/pc/mp.c:9 < #include "acpi.h" /sys/src/9/pc/mp.c:13,20 c /n/src/sources/plan9//sys/src/9/pc/mp.c:12 < static Lock mprdthilock; < static int mprdthi; < static Apic mpapic[MaxAPICNO+1]; < Apic* mplapics; < Apic* mpioapics; < static int lastlapic = -1; < static int lastioapic = -1; < static int nmpapic; --- > static PCMP* mppcmp; /sys/src/9/pc/mp.c:23 d /n/src/sources/plan9//sys/src/9/pc/mp.c:14 < static Ref mpvnoref; /* unique vector assignment */ /sys/src/9/pc/mp.c:25 a /n/src/sources/plan9//sys/src/9/pc/mp.c:17,23 > extern int i8259elcr; /* mask of level-triggered interrupts */ > static Apic mpapic[MaxAPICNO+1]; > static int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */ > static Lock mprdthilock; > static int mprdthi; > static Ref mpvnoref; /* unique vector assignment */ > static int mpmachno = 1; /sys/src/9/pc/mp.c:27 c /n/src/sources/plan9//sys/src/9/pc/mp.c:25,45 < static int debug; --- > static char* buses[] = { > "CBUSI ", > "CBUSII", > "EISA ", > "FUTURE", > "INTERN", > "ISA ", > "MBI ", > "MBII ", > "MCA ", > "MPI ", > "MPSA ", > "NUBUS ", > "PCI ", > "PCMCIA", > "TC ", > "VL ", > "VME ", > "XPRESS", > 0, > }; /sys/src/9/pc/mp.c:29,32 c /n/src/sources/plan9//sys/src/9/pc/mp.c:47,48 < #define dprint if(debug)print < < Apic* < mpnewapic(int type, int no, int flags) --- > static Apic* > mkprocessor(PCMPprocessor* p) /sys/src/9/pc/mp.c:36,53 c /n/src/sources/plan9//sys/src/9/pc/mp.c:52,64 < dprint("apic type %d no %d %#ux\n", type, no, flags); < apic = &mpapic[nmpapic]; < apic->type = type; < apic->apicno = no; < apic->flags = flags; < apic->lintr[0] = apic->lintr[1] = ApicIMASK; < if(type == PcmpPROCESSOR){ < if(lastlapic >= 0) < mpapic[lastlapic].next = apic; < else < mplapics = apic; < lastlapic = nmpapic; < }else{ < if(lastioapic >= 0) < mpapic[lastioapic].next = apic; < else < mpioapics = apic; < lastioapic = nmpapic; --- > if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) > return 0; > > apic = &mpapic[p->apicno]; > apic->type = PcmpPROCESSOR; > apic->apicno = p->apicno; > apic->flags = p->flags; > apic->lintr[0] = ApicIMASK; > apic->lintr[1] = ApicIMASK; > > if(p->flags & PcmpBP){ > machno2apicno[0] = p->apicno; > apic->machno = 0; /sys/src/9/pc/mp.c:55 c /n/src/sources/plan9//sys/src/9/pc/mp.c:66,71 < nmpapic++; --- > else{ > machno2apicno[mpmachno] = p->apicno; > apic->machno = mpmachno; > mpmachno++; > } > /sys/src/9/pc/mp.c:59,60 c /n/src/sources/plan9//sys/src/9/pc/mp.c:75,76 < static Apic* < mpgetapic(int type, int no) --- > static Bus* > mkbus(PCMPbus* p) /sys/src/9/pc/mp.c:62,76 d /n/src/sources/plan9//sys/src/9/pc/mp.c:77 < Apic *apic; < < if(type == PcmpPROCESSOR) < apic = mplapics; < else < apic = mpioapics; < for(; apic != nil; apic = apic->next) < if(apic->apicno == no) < return apic; < return nil; < } < < Bus* < mpnewbus(int type, int busno) < { /sys/src/9/pc/mp.c:77 a /n/src/sources/plan9//sys/src/9/pc/mp.c:79 > int i; /sys/src/9/pc/mp.c:79 c /n/src/sources/plan9//sys/src/9/pc/mp.c:81,87 < dprint("mpnewbus %d %d\n", type, busno); --- > for(i = 0; buses[i]; i++){ > if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) > break; > } > if(buses[i] == 0) > return 0; > /sys/src/9/pc/mp.c:87,98 c /n/src/sources/plan9//sys/src/9/pc/mp.c:95,96 < if(type == BusISA){ < if(mpisabus != -1) < print("mkbus: more than one ISA bus\n"); < mpisabus = busno; < } < if(type == BusEISA){ < if(mpeisabus != -1) < print("mkbus: more than one EISA bus\n"); < mpeisabus = busno; < } < bus->type = type; < bus->busno = busno; --- > bus->type = i; > bus->busno = p->busno; /sys/src/9/pc/mp.c:101 a /n/src/sources/plan9//sys/src/9/pc/mp.c:100,102 > if(mpeisabus != -1) > print("mkbus: more than one EISA bus\n"); > mpeisabus = bus->busno; /sys/src/9/pc/mp.c:109 a /n/src/sources/plan9//sys/src/9/pc/mp.c:111,113 > if(mpisabus != -1) > print("mkbus: more than one ISA bus\n"); > mpisabus = bus->busno; /sys/src/9/pc/mp.c:114 a /n/src/sources/plan9//sys/src/9/pc/mp.c:119 > /sys/src/9/pc/mp.c:119 c /n/src/sources/plan9//sys/src/9/pc/mp.c:124 < mpgetbus(int type, int busno) --- > mpgetbus(int busno) /sys/src/9/pc/mp.c:124,126 c /n/src/sources/plan9//sys/src/9/pc/mp.c:129 < if(type != -1 && type != bus->type) < continue; < if(busno == -1 || busno == bus->busno) --- > if(bus->busno == busno) /sys/src/9/pc/mp.c:131 c /n/src/sources/plan9//sys/src/9/pc/mp.c:134 < return nil; --- > return 0; /sys/src/9/pc/mp.c:133 a /n/src/sources/plan9//sys/src/9/pc/mp.c:137,210 > static Apic* > mkioapic(PCMPioapic* p) > { > Apic *apic; > void *va; > > if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) > return 0; > > /* > * Map the I/O APIC. > */ > if((va = vmap(p->addr, 1024)) == nil) > return 0; > > apic = &mpapic[p->apicno]; > apic->type = PcmpIOAPIC; > apic->apicno = p->apicno; > apic->addr = va; > apic->paddr = p->addr; > apic->flags = p->flags; > > return apic; > } > > static Aintr* > mkiointr(PCMPintr* p) > { > Bus *bus; > Aintr *aintr; > PCMPintr* pcmpintr; > > /* > * According to the MultiProcessor Specification, a destination > * I/O APIC of 0xFF means the signal is routed to all I/O APICs. > * It's unclear how that can possibly be correct so treat it as > * an error for now. > */ > if(p->apicno == 0xFF) > return 0; > if((bus = mpgetbus(p->busno)) == 0) > return 0; > > aintr = xalloc(sizeof(Aintr)); > aintr->intr = p; > > if(0) > print("iointr: type %d intr type %d flags %#o " > "bus %d irq %d apicno %d intin %d\n", > p->type, p->intr, p->flags, > p->busno, p->irq, p->apicno, p->intin); > /* > * Hack for Intel SR1520ML motherboard, which BIOS describes > * the i82575 dual ethernet controllers incorrectly. > */ > if(memcmp(mppcmp->product, "INTEL X38MLST ", 20) == 0){ > if(p->busno == 1 && p->intin == 16 && p->irq == 1){ > pcmpintr = malloc(sizeof(PCMPintr)); > memmove(pcmpintr, p, sizeof(PCMPintr)); > print("mkiointr: %20.20s bus %d intin %d irq %d\n", > (char*)mppcmp->product, > pcmpintr->busno, pcmpintr->intin, > pcmpintr->irq); > pcmpintr->intin = 17; > aintr->intr = pcmpintr; > } > } > aintr->apic = &mpapic[p->apicno]; > aintr->next = bus->aintr; > bus->aintr = aintr; > > return aintr; > } > /sys/src/9/pc/mp.c:208,209 c /n/src/sources/plan9//sys/src/9/pc/mp.c:285,286 < Aintr* < mpnewintr(PCMPintr *p) --- > static int > mklintr(PCMPintr* p) /sys/src/9/pc/mp.c:211,245 d /n/src/sources/plan9//sys/src/9/pc/mp.c:287 < Bus *bus; < Aintr *aintr; < < /* < * According to the MultiProcessor Specification, a destination < * I/O APIC of 0xFF means the signal is routed to all I/O APICs. < * It's unclear how that can possibly be correct so treat it as < * an error for now. < */ < if(p->apicno == 0xFF) < return 0; < if((bus = mpgetbus(-1, p->busno)) == 0){ < dprint("mpnewintr: bus %d not found\n", p->busno); < return 0; < } < < aintr = xalloc(sizeof(Aintr)); < aintr->intr = xalloc(sizeof(PCMPintr)); < *aintr->intr = *p; < < dprint("iointr: type %d intr type %d flags %#o " < "bus %d irq %d apicno %d intin %d\n", < p->type, p->intr, p->flags, < p->busno, p->irq, p->apicno, p->intin); < aintr->apic = mpgetapic(PcmpIOAPIC, p->apicno); < if(aintr->apic == nil) < print("mpnewintr: no ioapic %d\n", p->apicno); < aintr->next = bus->aintr; < bus->aintr = aintr; < return aintr; < } < < int < mpnewlintr(PCMPintr* p) < { /sys/src/9/pc/mp.c:248 c /n/src/sources/plan9//sys/src/9/pc/mp.c:290 < int intin, v, i; --- > int intin, v; /sys/src/9/pc/mp.c:254 c /n/src/sources/plan9//sys/src/9/pc/mp.c:296 < if((bus = mpgetbus(-1, p->busno)) == 0) --- > if((bus = mpgetbus(p->busno)) == 0) /sys/src/9/pc/mp.c:268,270 c /n/src/sources/plan9//sys/src/9/pc/mp.c:310,312 < for(i = 0; i <= MaxAPICNO; i++){ < apic = mpgetapic(PcmpPROCESSOR, i); < if(apic != nil && (apic->flags & PcmpEN)) --- > for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ > if((apic->flags & PcmpEN) > && apic->type == PcmpPROCESSOR) /sys/src/9/pc/mp.c:275,276 c /n/src/sources/plan9//sys/src/9/pc/mp.c:317,318 < apic = mpgetapic(PcmpPROCESSOR, p->apicno); < if(apic != nil && (apic->flags & PcmpEN)) --- > apic = &mpapic[p->apicno]; > if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR) /sys/src/9/pc/mp.c:336,343 d /n/src/sources/plan9//sys/src/9/pc/mp.c:377 < mprdthiw(Apic *apic, int intin, int lo) < { < lock(&mprdthilock); < ioapicrdtw(apic, intin, mprdthi, lo); < unlock(&mprdthilock); < } < < static void /sys/src/9/pc/mp.c:346,347 c /n/src/sources/plan9//sys/src/9/pc/mp.c:380 < if(debug) < iprint("Hello Squidboy\n"); --- > // iprint("Hello Squidboy\n"); /sys/src/9/pc/mp.c:391 d /n/src/sources/plan9//sys/src/9/pc/mp.c:423 < * /sys/src/9/pc/mp.c:399,400 c /n/src/sources/plan9//sys/src/9/pc/mp.c:431 < if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil){ < iprint("mpstartap: can't mmuwalk to mach\n"); --- > if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil) /sys/src/9/pc/mp.c:402 d /n/src/sources/plan9//sys/src/9/pc/mp.c:432 < } /sys/src/9/pc/mp.c:441 c /n/src/sources/plan9//sys/src/9/pc/mp.c:471 < iprint("mp: bad APBOOTSTRAP\n"); --- > print("mp: bad APBOOTSTRAP\n"); /sys/src/9/pc/mp.c:460 c /n/src/sources/plan9//sys/src/9/pc/mp.c:490 < mpstart(Apic *bpapic) --- > mpinit(void) /sys/src/9/pc/mp.c:464 c /n/src/sources/plan9//sys/src/9/pc/mp.c:494,497 < Apic *apic; --- > PCMP *pcmp; > uchar *e, *p; > Apic *apic, *bpapic; > void *va; /sys/src/9/pc/mp.c:466,467 c /n/src/sources/plan9//sys/src/9/pc/mp.c:499,578 < dprint("bpapic lint0 %#ux lint1 %#ux...\n", < bpapic->lintr[0], bpapic->lintr[1]); --- > i8259init(); > syncclock(); > > if(_mp_ == 0) > return; > pcmp = KADDR(_mp_->physaddr); > > /* > * Map the local APIC. > */ > if((va = vmap(pcmp->lapicbase, 1024)) == nil) > return; > mppcmp = pcmp; > print("LAPIC: %.8lux %.8lux\n", pcmp->lapicbase, (ulong)va); > > bpapic = nil; > > /* > * Run through the table saving information needed for starting > * application processors and initialising any I/O APICs. The table > * is guaranteed to be in order such that only one pass is necessary. > */ > p = ((uchar*)pcmp)+sizeof(PCMP); > e = ((uchar*)pcmp)+pcmp->length; > while(p < e) switch(*p){ > > default: > print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", > *p, e-p); > while(p < e){ > print("%uX ", *p); > p++; > } > break; > > case PcmpPROCESSOR: > if(apic = mkprocessor((PCMPprocessor*)p)){ > /* > * Must take a note of bootstrap processor APIC > * now as it will be needed in order to start the > * application processors later and there's no > * guarantee that the bootstrap processor appears > * first in the table before the others. > */ > apic->addr = va; > apic->paddr = pcmp->lapicbase; > if(apic->flags & PcmpBP) > bpapic = apic; > } > p += sizeof(PCMPprocessor); > continue; > > case PcmpBUS: > mkbus((PCMPbus*)p); > p += sizeof(PCMPbus); > continue; > > case PcmpIOAPIC: > if(apic = mkioapic((PCMPioapic*)p)) > ioapicinit(apic, ((PCMPioapic*)p)->apicno); > p += sizeof(PCMPioapic); > continue; > > case PcmpIOINTR: > mkiointr((PCMPintr*)p); > p += sizeof(PCMPintr); > continue; > > case PcmpLINTR: > mklintr((PCMPintr*)p); > p += sizeof(PCMPintr); > continue; > } > > /* > * No bootstrap processor, no need to go further. > */ > if(bpapic == 0) > return; > /sys/src/9/pc/mp.c:478 d /n/src/sources/plan9//sys/src/9/pc/mp.c:588 < dprint("local intrs...\n"); /sys/src/9/pc/mp.c:482 d /n/src/sources/plan9//sys/src/9/pc/mp.c:591 < dprint("lapic online...\n"); /sys/src/9/pc/mp.c:498,499 c /n/src/sources/plan9//sys/src/9/pc/mp.c:607 < conf.nmach = 1; < for(apic = mplapics; apic != nil; apic = apic->next){ --- > for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ /sys/src/9/pc/mp.c:502 c /n/src/sources/plan9//sys/src/9/pc/mp.c:610,611 < if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){ --- > if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN > && apic->type == PcmpPROCESSOR){ /sys/src/9/pc/mp.c:518,519 d /n/src/sources/plan9//sys/src/9/pc/mp.c:626 < < dprint("nmach = %ld\n", conf.nmach); /sys/src/9/pc/mp.c:539 c /n/src/sources/plan9//sys/src/9/pc/mp.c:646,651 < bus = mpgetbus(type, bno); --- > for(bus = mpbus; bus != nil; bus = bus->next){ > if(bus->type != type) > continue; > if(bus->busno == bno) > break; > } /sys/src/9/pc/mp.c:541 c /n/src/sources/plan9//sys/src/9/pc/mp.c:653 < print("mpintrenablex: can't find bus type %d\n", type); --- > print("ioapicirq: can't find bus type %d\n", type); /sys/src/9/pc/mp.c:594 c /n/src/sources/plan9//sys/src/9/pc/mp.c:706 < print("mpintrenablex: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", --- > print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", /sys/src/9/pc/mp.c:618 a /n/src/sources/plan9//sys/src/9/pc/mp.c:731 > //print("%s vector %d (imask)\n", v->name, vno); /sys/src/9/pc/mp.c:620 c /n/src/sources/plan9//sys/src/9/pc/mp.c:733 < print("mpintrenablex: vno %d, irq %d, tbdf %uX\n", --- > print("mpintrenable: vno %d, irq %d, tbdf %uX\n", /sys/src/9/pc/mp.c:632,633 c /n/src/sources/plan9//sys/src/9/pc/mp.c:745,749 < if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC) < mprdthiw(apic, aintr->intr->intin, lo); --- > if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){ > lock(&mprdthilock); > ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo); > unlock(&mprdthilock); > } /sys/src/9/pc/mp.c:729 d /n/src/sources/plan9//sys/src/9/pc/mp.c:844 < acpireset(); /sys/src/9/pc/mp.c:752,784 d /n/src/sources/plan9//sys/src/9/pc/mp.c:866 < < Lock mpsynclock; < < void < syncclock(void) < { < uvlong x; < < if(arch->fastclock != tscticks) < return; < < if(m->machno == 0){ < wrmsr(0x10, 0); < m->tscticks = 0; < } else { < x = MACHP(0)->tscticks; < while(x == MACHP(0)->tscticks) < ; < wrmsr(0x10, MACHP(0)->tscticks); < cycles(&m->tscticks); < } < } < < uvlong < tscticks(uvlong *hz) < { < if(hz != nil) < *hz = m->cpuhz; < < cycles(&m->tscticks); /* Uses the rdtsc instruction */ < return m->tscticks; < } < /sys/src/9/pc/mp.h:134 c /n/src/sources/plan9//sys/src/9/pc/mp.h:134 < * table or during parsing of ACPI tables. --- > * table. /sys/src/9/pc/mp.h:157 d /n/src/sources/plan9//sys/src/9/pc/mp.h:156 < Apic* next; /* next Apic of the same type */ /sys/src/9/pc/mp.h:168 d /n/src/sources/plan9//sys/src/9/pc/mp.h:166 < int ibase; /* I/O APIC: acpi irq. base */ /sys/src/9/pc/mp.h:212,237 c /n/src/sources/plan9//sys/src/9/pc/mp.h:210,212 < extern Aintr* mpnewintr(PCMPintr *p); < extern Apic* mpnewapic(int type, int no, int flags); < extern Bus* mpnewbus(int type, int busno); < extern int lapiceoi(int v); < extern int lapicisr(int v); < extern int mpintrenable(Vctl* v); < extern int mpnewlintr(PCMPintr* p); < extern uvlong tscticks(uvlong *hz); < extern void ioapicinit(Apic* apic, int apicno); < extern void ioapicrdtr(Apic* apic, int sel, int* hi, int* lo); < extern void ioapicrdtw(Apic* apic, int sel, int hi, int lo); < extern void lapicclock(Ureg *u, void*); < extern void lapicerror(Ureg*, void*); < extern void lapicicrw(ulong hi, ulong lo); < extern void lapicinit(Apic* apic); < extern void lapicintroff(void); < extern void lapicintron(void); < extern void lapicnmidisable(void); < extern void lapicnmienable(void); < extern void lapiconline(void); < extern void lapicspurious(Ureg*, void*); < extern void lapicstartap(Apic* apic, int v); < extern void lapictimerset(uvlong next); < extern void mpshutdown(void); < extern void mpstart(Apic *bpapic); < extern void syncclock(void); --- > extern void ioapicinit(Apic*, int); > extern void ioapicrdtr(Apic*, int, int*, int*); > extern void ioapicrdtw(Apic*, int, int, int); /sys/src/9/pc/mp.h:239,240 c /n/src/sources/plan9//sys/src/9/pc/mp.h:214,233 < extern Apic *mplapics; < extern Apic *mpioapics; --- > extern void lapicclock(Ureg*, void*); > extern int lapiceoi(int); > extern void lapicerror(Ureg*, void*); > extern void lapicicrw(ulong, ulong); > extern void lapicinit(Apic*); > extern void lapicintroff(void); > extern void lapicintron(void); > extern int lapicisr(int); > extern void lapicnmidisable(void); > extern void lapicnmienable(void); > extern void lapiconline(void); > extern void lapicspurious(Ureg*, void*); > extern void lapicstartap(Apic*, int); > extern void lapictimerset(uvlong); > > extern void mpinit(void); > extern int mpintrenable(Vctl*); > extern void mpshutdown(void); > > extern _MP_ *_mp_; /sys/src/9/pc/pcf:4,6 d /n/src/sources/plan9//sys/src/9/pc/pcf:3 < env < p9am acpi < /sys/src/9/pc/pcf:8,12 d /n/src/sources/plan9//sys/src/9/pc/pcf:4 < proc < dup < srv < mnt < pipe /sys/src/9/pc/pcf:14 a /n/src/sources/plan9//sys/src/9/pc/pcf:7,12 > env > pipe > proc > mnt > srv > dup /sys/src/9/pc/pcf:54 c /n/src/sources/plan9//sys/src/9/pc/pcf:52,53 < ether82543gc pci --- > # should be obsoleted by igbe > # ether82543gc pci /sys/src/9/pc/pcf:104 a /n/src/sources/plan9//sys/src/9/pc/pcf:104 > vgaradeon +cur /sys/src/9/pc/pcf:136,138 c /n/src/sources/plan9//sys/src/9/pc/pcf:136,137 < /386/bin/9am < < --- > /386/bin/venti/venti > /386/bin/usb/usbd /sys/src/9/port/chan.c:171 d /n/src/sources/plan9//sys/src/9/port/chan.c:170 < static Lock l; /sys/src/9/port/chan.c:190 c /n/src/sources/plan9//sys/src/9/port/chan.c:189 < chandevreset(int sw) --- > chandevreset(void) /sys/src/9/port/chan.c:193 a /n/src/sources/plan9//sys/src/9/port/chan.c:193 > todinit(); /* avoid later reentry causing infinite recursion */ /sys/src/9/port/chan.c:195,196 c /n/src/sources/plan9//sys/src/9/port/chan.c:195 < if((sw != 0 && i >= bootdevtabs) || (sw == 0 && i < bootdevtabs)) < devtab[i]->reset(); --- > devtab[i]->reset(); /sys/src/9/port/chan.c:200 c /n/src/sources/plan9//sys/src/9/port/chan.c:199 < chandevinit(int sw) --- > chandevinit(void) /sys/src/9/port/chan.c:204 d /n/src/sources/plan9//sys/src/9/port/chan.c:202 < todinit(); /* avoid later reentry causing infinite recursion */ /sys/src/9/port/chan.c:206,207 c /n/src/sources/plan9//sys/src/9/port/chan.c:204 < if((sw != 0 && i >= bootdevtabs) || (sw == 0 && i < bootdevtabs)) < devtab[i]->init(); --- > devtab[i]->init(); /sys/src/9/port/initcode.c:11 d /n/src/sources/plan9//sys/src/9/port/initcode.c:10 < char p9am[] = "/boot/9am"; /sys/src/9/port/initcode.c:35,36 d /n/src/sources/plan9//sys/src/9/port/initcode.c:33 < argv[0] = boot; < exec(p9am, argv); /sys/src/9/port/mkdevc:7,9 d /n/src/sources/plan9//sys/src/9/port/mkdevc:6 < dev[ndev++] = "root"; < dev[ndev++] = "env"; < bootdevtabs = ndev; /sys/src/9/port/mkdevc:22,26 c /n/src/sources/plan9//sys/src/9/port/mkdevc:19 < for(i = 0; i < ndev; i++) < if(dev[i] == $1) < break; < if(i == ndev) < dev[ndev++] = $1; --- > dev[ndev++] = $1; /sys/src/9/port/mkdevc:95 d /n/src/sources/plan9//sys/src/9/port/mkdevc:87 < printf "int bootdevtabs=%d;\n", bootdevtabs; /sys/src/9/port/mkrootc:45,48 d /n/src/sources/plan9//sys/src/9/port/mkrootc:44 < static int done; < < if(done++) < return; /sys/src/9/port/portdat.h:760 d /n/src/sources/plan9//sys/src/9/port/portdat.h:759 < extern int bootdevtabs; /sys/src/9/port/portfns.h:21 d /n/src/sources/plan9//sys/src/9/port/portfns.h:20 < void* realloc(void*, ulong); /sys/src/9/port/portfns.h:26,27 c /n/src/sources/plan9//sys/src/9/port/portfns.h:25,26 < void chandevinit(int); < void chandevreset(int); --- > void chandevinit(void); > void chandevreset(void); rc 8168: diff 8182: some 9am/9am.c 644 0 0 15647 11333076465 10247ustar00nemosys/* * ACPI configurator and AML interpreter */ #include "kernel.h" #include "acpi.h" #include "aml.h" enum { Maxtblsz = 64 * 1024, }; typedef struct Parse Parse; struct Parse { char* sig; int (*f)(uchar*, int); /* return nil to keep vmap */ }; Biobuf* bout; Biobuf boutbuf; static Parse ptables[] = { "SRAT", acpisrat, "MSCT", acpimsct, "DSDT", acpidsdt, "SSDT", acpidsdt, }; static void usage(void) { fprint(2, "usage: %s: [-AESbcdinosx] [-f file] [op...]\n", argv0); fprint(2, "where op is\n"); fprint(2, "\t/a/path\tto dump the ns rooted there\n"); fprint(2, "\t%%/a/path\tto configure devices in that tree\n"); fprint(2, "\t!/method/path\tto call a method\n"); fprint(2, "\t@/method/path\tto dump source for a method\n"); fprint(2, "\t+/method/path\tto set tracing for a method\n"); fprint(2, "\t-/method/path\tto clear tracing for a method\n"); exits(nil); } static int readtable(Biobuf *bin) { Parse *p; char *ln; char *toks[50]; int ntoks; int n; long len; int i; uchar *amlblock; int ishexdump; while((ln = Brdstr(bin, '\n', 1)) != nil){ for(i = 0; i < nelem(ptables); i++){ p = &ptables[i]; if(strncmp(ln, p->sig, 4) == 0) goto Found; } free(ln); } return -1; Found: len = Maxtblsz; /* BUG: should allocate only what we need */ dprint("reading %s...\n", ln); free(ln); amlblock = malloc(len); if(amlblock == nil) sysfatal("no memory"); n = ishexdump = 0; while(n < len && (ln = Brdstr(bin, '\n', 1)) != nil){ if(ishexdump && strlen(ln) > 56) ln[56] = 0; ntoks = tokenize(ln, toks, nelem(toks)); if(ntoks <= 0) break; if(ntoks > 17){ ishexdump = 1; ntoks = 17; } for(i = 1; n < len && i < ntoks; i++){ if(strlen(toks[i]) > 2) break; amlblock[n++] = strtol(toks[i], nil, 16); //Bprint(bout, "%s ", toks[i]); } //Bprint(bout, "\n"); free(ln); } if(n <= Sdthdrsz){ free(amlblock); return -1; } if(p->f(amlblock, n) <= 0) free(amlblock); return 0; } static int dumpmethod(Aobj *o, int flags) { xprint("method %s/%d:\n", o->name->path, o->method.nargs); if(o->method.f != nil) xprint("\tbuiltin\n"); else amleval(o->method.ps, o->method.pe, nil, o->name, nil, flags); xprint("\n"); return 1; } /* * We leak args here. */ static int fetchargs(int nargs, Aobj **args, int argc, char **argv) { int n; for(n = 0; n < nargs && n < argc; n++) if(argv[n][0] == '0') args[n] = amlnewi(strtoul(argv[n], 0, 0)); else args[n] = amlnews(argv[n]); if(n < nargs) return -1; return 0; } static void loadtables(char *fname, int amlflags) { Biobuf *bin; bin = Bopen(fname, OREAD); if(bin == nil) sysfatal("%s: %s: %r", argv0, fname); amlinit(); acpiflags = amlflags; dprint("%s: reading %s...\n", argv0, fname); while(readtable(bin) == 0) Bflush(bout); Bterm(bin); } static void acpi(char *fname, int amlflags, int docfg) { acpiflags = amlflags; if(acpiinit() < 0) return; loadtables(fname, amlflags); if(docfg == 0) return; dxprint("9am acpi gpes...\n"); acpigpes(); dxprint("9am acpi pwrs...\n"); acpipwrs(); dxprint("9am acpi apics...\n"); acpiapics(); dxprint("9am acpi conf...\n"); acpiconf(acpins); dxprint("9am acpi done\n"); } static void cmds(int argc, char *argv[], int amlflags) { Aobj*args[Nmargs], *o; int x, i, na, flags; Aname *n; for(i = 0; i < argc; i++){ x = argv[i][0]; if(x == '!' || x == '@' || x == '%' || x == '+' || x == '-') argv[i]++; n = amlwalk(argv[i], Walkns, acpins); if(n == nil){ xprint("%s: name not found\n", argv[i]); continue; } if(x == '!' || x == '@' || x == '+' || x == '-') if(n->o->type != Omethod){ xprint("%s: not a method\n", argv[i]); break; } switch(x){ case '!': flags = amlflags; memset(args, 0, sizeof(args)); na = n->o->method.nargs; if(fetchargs(na, args, argc-i-1, argv+i+1)<0){ xprint("%s: not enough args\n", argv[i]); break; } o = amlcall(n->o, args, nil, flags); if(o == nil) xprint("%s: %s\n", argv[i], amlerrstk.msg); else xprint("%s: %O\n", argv[i], o); amlclose(o); i += na; break; case '@': flags = amlflags|Emdump|Eidump|Enoeval; dumpmethod(n->o, flags); break; case '+': n->o->method.trace = 1; /* force on */ break; case '-': n->o->method.trace = -1; /* force off */ break; case '%': acpiflags = amlflags; acpiconf(n); break; default: dumpns(n, 0, 1); } FLUSH; } } static int bind9am(void) { bind("#ec", "/env", MREPL); bind("#e", "/env", MBEFORE|MCREATE); if(access("/dev/acpictl", AWRITE) == 0) return 0; return bind("#9", "/dev", MAFTER); } static void checkno9am(char *argv[]) { char *p; p = getenv("*9am"); if(p == nil || p[0] != '1' || bind9am() < 0){ fprint(2, "no 9am\n"); exec("/boot/boot", argv); exits("noboot"); } print("9am...\n"); free(p); } static void hwinit(void) { int fd; dxprint("9am: hwinit...\n"); fd = open("#9/acpictl", OWRITE); if(fd < 0) sysfatal("can't open ctl: %r"); if(write(fd, "init", 4) != 4) sysfatal("can't hwinit"); close(fd); dxprint("9am: hwinit done\n"); } void main(int argc, char *argv[]) { char **bootargv; char *p9args[10]; char *p, *fname; int sflag, amlflags, cfgflags; acpistackbottom = (char*)&argc; /* debug */ /* * If we are called as /boot/boot, argv is for * the real /boot/boot, and our args come from 9amargs. */ argv0 = "9am"; bootargv = nil; if(strcmp(argv[0], "/boot/boot") == 0){ bootargv = argv; checkno9am(bootargv); argv = p9args; p9args[0] = "9am"; argc = 1; p = getenv("*debug9am"); if(p != nil) argc += tokenize(p, p9args+1, nelem(p9args)-2); p9args[argc] = nil; } amlflags = sflag = 0; fname = nil; ARGBEGIN{ case 'f': /* use file to read tables */ fname = EARGF(usage()); break; /* only useful for tracing and debugging */ case 's': /* dump stats */ sflag++; break; case 'n': /* do not perform I/O to /dev */ amlflags |= Edryrun; break; case 'd': /* activate "dprint" messages */ amlflags |= Evdump; break; case 'i': /* dump instructions for tables */ amlflags |= Eidump; break; case 'x': /* report actions */ amlflags |= Exdump; break; case 'b': /* report ns changes */ amlflags |= Ebdump; break; case 'o': amlflags |= Eodump; /* report object i/o */ break; /* more debugging */ case 'A': /* abort on errors */ amlflags |= Eabort; break; case 'E': /* dump environments after ops */ amlflags |= Eedump; break; case 'S': /* check out the stack (slow!!) */ amlflags |= Escheck; break; default: usage(); }ARGEND; if(fname == nil){ if(bind9am() < 0) sysfatal("no #9"); fname = "/dev/acpitbl"; } Binit(&boutbuf, 1, OWRITE); bout = &boutbuf; cfgflags = amlflags; if(argc == 0) cfgflags &= Edryrun|Eabort|Escheck; acpi(fname, cfgflags, argc == 0); cmds(argc, argv, amlflags); if(sflag){ dumpstats(); dumpopstats(); } Bterm(bout); if(bootargv != nil){ hwinit(); dprint("boot...\n"); exec("/boot/boot", bootargv); exits("noboot"); } exits(nil); } ys/src/9/port/chan.c:170 < static Lock l; /sys/src/9/port/chan.c:190 c /n/src/sources/pl9am/9pc/ 775 0 0 0 11334032360 100405ustar00nemosys9am/9pc/acpi.c 664 0 0 76470 11334032360 11156ustar00nemosys/* * ACPI 4.0 Hardware configuration. * * Parse tables needed for booting and provide * data for p9am(3) and devices. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "mp.h" #include "acpi.h" typedef struct Apicst Apicst; typedef struct Atable Atable; typedef struct Facs Facs; typedef struct Fadt Fadt; typedef struct Gas Gas; typedef struct Gpe Gpe; typedef struct Madt Madt; typedef struct Parse Parse; typedef struct Pwr Pwr; typedef struct Rsdp Rsdp; typedef struct Sdthdr Sdthdr; #pragma varargck type "G" Gas* enum { Npwr = 6, /* number of power states */ Sdthdrsz = 36, /* size of SDT header */ /* ACPI PM1 control */ Pm1SciEn = 0x1, /* Generate SCI and not SMI */ /* FADT bootarch flags */ BAlegacy = 1, BAkbd = 2, BAnovga = 4, /* Apic structure types */ ASlapic = 0, /* processor local apic */ ASioapic, /* I/O apic */ ASintovr, /* Interrupt source override */ ASnmi, /* NMI source */ ASlnmi, /* local apic nmi */ ASladdr, /* local apic address override */ ASiosapic, /* I/O sapic */ ASlsapic, /* local sapic */ ASintsrc, /* platform interrupt sources */ ASlx2apic, /* local x2 apic */ ASlx2nmi, /* local x2 apic NMI */ }; struct Parse { char* sig; void (*f)(uchar*, int); }; struct Gpe { uintptr stsio; /* port used for status */ int stsbit; /* bit number */ uintptr enio; /* port used for enable */ int enbit; /* bit number */ int nb; /* event number */ char* obj; /* handler object */ int id; /* id as supplied by user */ }; struct Pwr { int pm1a; /* value for pm1a.slp_typ to enter state */ int pm1b; /* value for pm1b.slp_typ to enter state */ }; /* * Generic address structure. */ struct Gas { u8int spc; /* address space id */ u8int len; /* register size in bits */ u8int off; /* bit offset */ u8int accsz; /* 1: byte; 2: word; 3: dword; 4: qword */ u64int addr; /* address (or acpi encoded tbdf + reg) */ }; /* Multiple APIC description table (sw) * ACPI system interrupt numbers are virtual, with each * ioapic knowing the base number for its first irq. */ struct Madt { u64int laddr; /* local APIC addr */ int pcat; /* also has PC/AT 8259s */ Apicst* st; }; struct Apicst { int type; Apicst* next; union{ struct{ int pid; /* processor id */ int id; /* apic id (<= 255) */ } lapic; struct{ int id; /* io apic id */ u32int ibase; /* interrupt base addr. */ u64int addr; /* base address */ } ioapic, iosapic; struct{ int irq; /* bus intr. source */ int intr; /* system interrupt */ int flags; /* INTI flags */ } intovr; struct{ int intr; /* system interrupt */ int flags; /* apic flags */ } nmi; struct{ int pid; /* processor id */ int flags; /* LINT flags */ int lint; /* apic LINTn for nmi */ } lnmi; struct{ int pid; /* processor id */ int id; /* apic id */ int eid; /* apic eid */ int puid; /* processor uid */ char* puids; /* same thing */ } lsapic; struct{ int pid; /* processor id */ int peid; /* processor eid */ int iosv; /* io sapic vector */ int intr; /* global sys intr. */ int type; /* intr type */ int flags; /* apic flags */ int any; /* err sts at any proc */ } intsrc; struct{ int id; /* x2 apic id (lapic >255) */ int puid; /* processor uid */ } lx2apic; struct{ int puid; int flags; int intr; } lx2nmi; }; }; /* Fixed ACPI description table (sw version) * Describes implementation and hardware registers. * PM* blocks are low level functions. * GPE* blocks refer to general purpose events. * P_* blocks are for processor features. * Contains the address for the DSDT. */ struct Fadt { u32int facs; u32int dsdt; /* 1 reserved */ u8int pmprofile; u16int sciint; u32int smicmd; u8int acpienable; u8int acpidisable; u8int s4biosreq; u8int pstatecnt; u32int pm1aevtblk; u32int pm1bevtblk; u32int pm1acntblk; u32int pm1bcntblk; u32int pm2cntblk; u32int pmtmrblk; u32int gpe0blk; u32int gpe1blk; u8int pm1evtlen; u8int pm1cntlen; u8int pm2cntlen; u8int pmtmrlen; u8int gpe0blklen; u8int gpe1blklen; u8int gp1base; u8int cstcnt; u16int plvl2lat; u16int plvl3lat; u16int flushsz; u16int flushstride; u8int dutyoff; u8int dutywidth; u8int dayalrm; u8int monalrm; u8int century; u16int iapcbootarch; /* 1 reserved */ u32int flags; Gas resetreg; u8int resetval; /* 3 reserved */ u64int xfacs; u64int xdsdt; Gas xpm1aevtblk; Gas xpm1bevtblk; Gas xpm1acntblk; Gas xpm1bcntblk; Gas xpm2cntblk; Gas xpmtmrblk; Gas xgpe0blk; Gas xgpe1blk; }; /* * ACPI table (sw) */ struct Atable { Atable* next; /* next table in list */ int is64; /* uses 64bits */ char sig[5]; /* signature */ char oemid[7]; /* oem id str. */ char oemtblid[9]; /* oem tbl. id str. */ uchar* p; /* table data, including hdr */ long len; /* size of data in table, including hdr */ }; /* * hardware structures */ /* Root system description table pointer. * Used to locate the root system description table RSDT * (or the extended system description table from version 2) XSDT. * The XDST contains (after the DST header) a list of pointers to tables: * - FADT fixed acpi description table. * It points to the DSDT, AML code for the acpi namespace. * - SSDTs tables with AML code to add to the acpi namespace. * - pointers to other tables (Apics, etc.) */ struct Rsdp { u8int signature[8]; /* "RSD PTR " */ u8int rchecksum; u8int oemid[6]; u8int revision; u8int raddr[4]; /* RSDT */ u8int length[4]; u8int xaddr[8]; /* XSDT */ u8int xchecksum; /* XSDT */ u8int _33_[3]; /* reserved */ }; /* Header for ACPI description tables */ struct Sdthdr { u8int sig[4]; /* "FACP" or whatever */ u8int length[4]; u8int rev; u8int csum; u8int oemid[6]; u8int oemtblid[8]; u8int oemrev[4]; u8int creatorid[4]; u8int creatorrev[4]; }; /* Firmware control structure */ struct Facs { u32int hwsig; u32int wakingv; u32int glock; u32int flags; u64int xwakingv; u8int vers; u32int ospmflags; }; static void acpifadt(uchar*, int); static void acpimadt(uchar*, int); static void acpitable(uchar*, int); #define l16get(p) (((p)[1]<<8)|(p)[0]) #define l32get(p) (((u32int)l16get(p+2)<<16)|l16get(p)) #pragma varargck type "I" uint #define dprint if(debug)print #define BIOSSEG(a) KADDR(((uint)(a))<<4) /* tables listed here are loaded from the XSDT */ static Parse ptables[] = { "FACP", acpifadt, "APIC", acpimadt, "SSDT", acpitable, "SRAT", acpitable, "MSCT", acpitable, }; static char *arnames[] = { "irq", "dma", "io", "mem", "memas", "ioas", "busas", "pir", "bbn" }; static Facs *facs; /* Firmware ACPI control structure */ static Fadt fadt; /* Fixed ACPI description. ACPI registers */ static Madt *apics; /* Fixed MADT entries */ static Atable *tfirst; /* loaded DSDT/SSDT/... tables */ static Atable *tlast; /* pointer to last table loaded */ static Gpe *gpes; /* General purpose events */ static int ngpes; static Pwr slptyp[Npwr]; /* write to pm1.slptyp to enter Sn */ static int debug = 1; int legacydevices; int keyboardpresent; int novgapresent; static int Gfmt(Fmt* f) { Gas *g; ulong x; g = va_arg(f->args, Gas*); switch(g->spc){ case Rsysmem: case Rsysio: case Rembed: case Rsmbus: case Rcmos: case Rpcibar: case Ripmi: fmtprint(f, "[%s ", acpiregstr(g->spc)); break; case Rpcicfg: x = g->addr >> 16; fmtprint(f, "[pci "); fmtprint(f, "dev %#ulx ", ACPIpcidev(x)); fmtprint(f, "fn %#ulx ", ACPIpcifn(x)); x = g->addr & 0xFFFF; fmtprint(f, "adr %#ulx ", x); break; case Rfixedhw: fmtprint(f, "[hw "); break; default: fmtprint(f, "[spc=%#ux ", g->spc); } return fmtprint(f, "off %d len %d addr %#ullx sz %d]", g->off, g->len, g->addr, g->accsz); } /* * interrupt flags */ static int Ifmt(Fmt *f) { int r; char flg[] = "--"; r = va_arg(f->args, int); if(r == -1) return fmtstrcpy(f, "none"); if((r&PcmpELMASK) == PcmpEDGE) flg[0] = 'e'; if((r&PcmpELMASK) == PcmpLEVEL) flg[0] = 'l'; if((r&PcmpPOMASK) == PcmpHIGH) flg[1] = 'h'; if((r&PcmpPOMASK) == PcmpLOW) flg[1] = 'l'; return fmtstrcpy(f, flg); } static int Rfmt(Fmt *f) { ACPIres *r; int i; r = va_arg(f->args, ACPIres*); switch(r->type){ case Rirq: fmtprint(f, "irq flg %#ux %I", r->irq.flags, r->irq.flags); for(i = 0; i < nelem(r->irq.nb); i++) if(r->irq.nb[i] == ~0) break; else fmtprint(f, " %d", r->irq.nb[i]); break; case Rdma: fmtprint(f, "dma chans %#ux flags %#ux", r->dma.chans, r->dma.flags); break; case Rio: case Rmem: fmtprint(f, "%s %s min %#ullx max %ullx align %ud", arnames[r->type], r->mem.isrw ? "rw" : "ro", r->mem.min, r->mem.max, r->mem.align); break; case Rmemas: case Rioas: case Rbusas: fmtprint(f, "%s flags %#ux [%#ullx-%ullx]" " %#ullx %+#ld attr %#ullx", arnames[r->type], r->as.flags, r->as.min, r->as.max, r->as.len, r->as.off, r->as.attr); break; case Rpir: fmtprint(f, "pir dev %d pin %d link %d aint %d", r->pir.dno, r->pir.pin, r->pir.link, r->pir.airq); break; case Rbbn: fmtprint(f, "bbn %d", r->bbn); break; default: fmtprint(f, "unknown res %#ux", r->type); } return 0; } static char* seprinttable(char *s, char *e, Atable *t) { uchar *p; int i; p = t->p; s = seprint(s, e, "%s @ %#p\n", t->sig, p); for(i = 0; i < t->len; i++){ if((i % 16) == 0) s = seprint(s, e, "%x: ", i); s = seprint(s, e, " %2.2ux", p[i]); if((i % 16) == 15) s = seprint(s, e, "\n"); } return seprint(s, e, "\n\n"); } char* smprinttables(void) { char *ttext; char *ns, *s, *e, *ntext; Atable *t; int tlen; tlen = 16*1024; ttext = malloc(tlen); if(ttext == nil){ print("acpi: no memory\n"); return 0; } s = ttext; e = ttext + tlen; strcpy(s, "no tables\n"); for(t = tfirst; t != nil; t = t->next){ ns = seprinttable(s, e, t); while(ns >= e - 1){ dprint("tables: grow %#p %d\n", ttext, tlen*2); ntext = realloc(ttext, tlen*2); if(ntext == nil) panic("acpi: no memory\n"); s = ntext + (s - ttext); ttext = ntext; tlen *= 2; e = ttext + tlen; ns = seprinttable(s, e, t); } s = ns; } return ttext; } static u64int l64get(u8int* p) { /* * Doing this as a define * #define l64get(p) (((u64int)l32get(p+4)<<32)|l32get(p)) * causes 8c to abort with "out of fixed registers". */ return (((u64int)l32get(p+4)<<32)|l32get(p)); } static Atable* newtable(uchar *p) { Atable *t; Sdthdr *h; t = malloc(sizeof(Atable)); if(t == nil) panic("no memory for aml tables"); t->p = nil; h = (Sdthdr*)p; t->is64 = h->rev >= 2; t->len = l32get(h->length); memmove(t->sig, h->sig, sizeof(h->sig)); t->sig[sizeof(t->sig)-1] = 0; memmove(t->oemid, h->oemid, sizeof(h->oemid)); t->oemtblid[sizeof(t->oemtblid)-1] = 0; memmove(t->oemtblid, h->oemtblid, sizeof(h->oemtblid)); t->oemtblid[sizeof(t->oemtblid)-1] = 0; t->next = nil; if(tfirst == nil) tfirst = tlast = t; else{ tlast->next = t; tlast = t; } return t; } static void* sdtchecksum(void* addr, int len) { u8int *p, sum; sum = 0; for(p = addr; len-- > 0; p++) sum += *p; if(sum == 0) return addr; return nil; } static void* sdtmap(uintptr pa, int *n, int cksum) { Sdthdr* sdt; sdt = vmap(pa, sizeof(Sdthdr)); if(sdt == nil) return nil; *n = l32get(sdt->length); vunmap(sdt, sizeof(Sdthdr)); if((sdt = vmap(pa, *n)) == nil) return nil; if(cksum != 0 && sdtchecksum(sdt, *n) == nil){ dprint("acpi: SDT: bad checksum\n"); vunmap(sdt, sizeof(Sdthdr)); return nil; } return sdt; } static int loadfacs(uintptr pa) { int n; facs = sdtmap(pa, &n, 0); if(facs == nil) return -1; if(memcmp(facs, "FACS", 4) != 0){ vunmap(facs, n); facs = nil; return -1; } /* don't vunmap */ if(debug){ print("acpi: facs\n"); print("\thwsig: %#ux\n", facs->hwsig); print("\twakingv: %#ux\n", facs->wakingv); print("\tflags: %#ux\n", facs->flags); print("\tglock: %#ux\n", facs->glock); print("\txwakingv: %#llux\n", facs->xwakingv); print("\tvers: %#ux\n", facs->vers); print("\tospmflags: %#ux\n", facs->ospmflags); print("\n"); } return 0; } static void loaddsdt(uintptr pa) { int n; uchar *dsdtp; dsdtp = sdtmap(pa, &n, 1); if(dsdtp == nil) return; acpitable(dsdtp, n); vunmap(dsdtp, n); } static void gasget(Gas *gas, uchar *p) { gas->spc = p[0]; gas->len = p[1]; gas->off = p[2]; gas->accsz = p[3]; gas->addr = l64get(p+4); } static void dumpfadt(Fadt *fp) { print("acpi fadt:\n"); print("\tfacs: %#ux\n", fp->facs); print("\tdsdt: %#ux\n", fp->dsdt); print("\tpmprofile: %#ux\n", fp->pmprofile); print("\tsciint: %#ux\n", fp->sciint); print("\tsmicmd: %#ux\n", fp->smicmd); print("\tacpienable: %#ux\n", fp->acpienable); print("\tacpidisable: %#ux\n", fp->acpidisable); print("\ts4biosreq: %#ux\n", fp->s4biosreq); print("\tpstatecnt: %#ux\n", fp->pstatecnt); print("\tpm1aevtblk: %#ux\n", fp->pm1aevtblk); print("\tpm1bevtblk: %#ux\n", fp->pm1bevtblk); print("\tpm1acntblk: %#ux\n", fp->pm1acntblk); print("\tpm1bcntblk: %#ux\n", fp->pm1bcntblk); print("\tpm2cntblk: %#ux\n", fp->pm2cntblk); print("\tpmtmrblk: %#ux\n", fp->pmtmrblk); print("\tgpe0blk: %#ux\n", fp->gpe0blk); print("\tgpe1blk: %#ux\n", fp->gpe1blk); print("\tpm1evtlen: %#ux\n", fp->pm1evtlen); print("\tpm1cntlen: %#ux\n", fp->pm1cntlen); print("\tpm2cntlen: %#ux\n", fp->pm2cntlen); print("\tpmtmrlen: %#ux\n", fp->pmtmrlen); print("\tgpe0blklen: %#ux\n", fp->gpe0blklen); print("\tgpe1blklen: %#ux\n", fp->gpe1blklen); print("\tgp1base: %#ux\n", fp->gp1base); print("\tcstcnt: %#ux\n", fp->cstcnt); print("\tplvl2lat: %#ux\n", fp->plvl2lat); print("\tplvl3lat: %#ux\n", fp->plvl3lat); print("\tflushsz: %#ux\n", fp->flushsz); print("\tflushstride: %#ux\n", fp->flushstride); print("\tdutyoff: %#ux\n", fp->dutyoff); print("\tdutywidth: %#ux\n", fp->dutywidth); print("\tdayalrm: %#ux\n", fp->dayalrm); print("\tmonalrm: %#ux\n", fp->monalrm); print("\tcentury: %#ux\n", fp->century); print("\tiapcbootarch: %#ux\n", fp->iapcbootarch); print("\tflags: %#ux\n", fp->flags); print("\tresetreg: %G\n", &fp->resetreg); print("\tresetval: %#ux\n", fp->resetval); print("\txfacs: %#llux\n", fp->xfacs); print("\txdsdt: %#llux\n", fp->xdsdt); print("\txpm1aevtblk: %G\n", &fp->xpm1aevtblk); print("\txpm1bevtblk: %G\n", &fp->xpm1bevtblk); print("\txpm1acntblk: %G\n", &fp->xpm1acntblk); print("\txpm1bcntblk: %G\n", &fp->xpm1bcntblk); print("\txpm2cntblk: %G\n", &fp->xpm2cntblk); print("\txpmtmrblk: %G\n", &fp->xpmtmrblk); print("\txgpe0blk: %G\n", &fp->xgpe0blk); print("\txgpe1blk: %G\n", &fp->xgpe1blk); print("\n"); } static void acpifadt(uchar *p, int) { Fadt *fp; fp = &fadt; fp->facs = l32get(p + 36); fp->dsdt = l32get(p + 40); fp->pmprofile = p[45]; fp->sciint = l16get(p+46); fp->smicmd = l32get(p+48); fp->acpienable = p[52]; fp->acpidisable = p[53]; fp->s4biosreq = p[54]; fp->pstatecnt = p[55]; fp->pm1aevtblk = l32get(p+56); fp->pm1bevtblk = l32get(p+60); fp->pm1acntblk = l32get(p+64); fp->pm1bcntblk = l32get(p+68); fp->pm2cntblk = l32get(p+72); fp->pmtmrblk = l32get(p+76); fp->gpe0blk = l32get(p+80); fp->gpe1blk = l32get(p+84); fp->pm1evtlen = p[88]; fp->pm1cntlen = p[89]; fp->pm2cntlen = p[90]; fp->pmtmrlen = p[91]; fp->gpe0blklen = p[92]; fp->gpe1blklen = p[93]; fp->gp1base = p[94]; fp->cstcnt = p[95]; fp->plvl2lat = l16get(p+96); fp->plvl3lat = l16get(p+98); fp->flushsz = l16get(p+100); fp->flushstride = l16get(p+102); fp->dutyoff = p[104]; fp->dutywidth = p[105]; fp->dayalrm = p[106]; fp->monalrm = p[107]; fp->century = p[108]; fp->iapcbootarch = l16get(p+109); fp->flags = l32get(p+112); gasget(&fp->resetreg, p+116); fp->resetval = p[128]; fp->xfacs = l64get(p+132); fp->xdsdt = l64get(p+140); gasget(&fp->xpm1aevtblk, p+148); gasget(&fp->xpm1bevtblk, p+160); gasget(&fp->xpm1acntblk, p+172); gasget(&fp->xpm1bcntblk, p+184); gasget(&fp->xpm2cntblk, p+196); gasget(&fp->xpmtmrblk, p+208); gasget(&fp->xgpe0blk, p+220); gasget(&fp->xgpe1blk, p+232); legacydevices = fp->iapcbootarch & BAlegacy; keyboardpresent = fp->iapcbootarch & BAkbd; novgapresent = fp->iapcbootarch & BAnovga; if(debug) dumpfadt(fp); if(fp->xfacs != 0) loadfacs(fp->xfacs); else loadfacs(fp->facs); if(fp->xdsdt != 0) loaddsdt(fp->xdsdt); else loaddsdt(fp->dsdt); } static void dumpmadt(Madt *apics) { Apicst *st; print("acpi: madt lapic addr %llux pcat %d:\n", apics->laddr, apics->pcat); for(st = apics->st; st != nil; st = st->next) switch(st->type){ case ASlapic: print("\tlapic pid %d id %d\n", st->lapic.pid, st->lapic.id); break; case ASioapic: case ASiosapic: print("\tioapic id %d addr %#llux ibase %d\n", st->ioapic.id, st->ioapic.addr, st->ioapic.ibase); break; case ASintovr: print("\tintovr irq %d intr %d %##ux\n", st->intovr.irq, st->intovr.intr, st->intovr.flags); break; case ASnmi: print("\tnmi intr %d %##ux\n", st->nmi.intr, st->nmi.flags); break; case ASlnmi: print("\tlnmi pid %d lint %d %#ux\n", st->lnmi.pid, st->lnmi.lint, st->lnmi.flags); break; case ASlsapic: print("\tlsapic pid %d id %d eid %d" " puid %d puids %s\n", st->lsapic.pid, st->lsapic.id, st->lsapic.eid, st->lsapic.puid, st->lsapic.puids); break; case ASintsrc: print("\tintr type %d pid %d peid %d" " iosv %d intr %d %#ux\n", st->type, st->intsrc.pid, st->intsrc.peid, st->intsrc.iosv, st->intsrc.intr, st->intsrc.flags); break; case ASlx2apic: print("\tlx2apic puid %d id %d\n", st->lx2apic.puid, st->lx2apic.id); break; case ASlx2nmi: print("\tlx2nmi puid %d intr %d %##ux\n", st->lx2nmi.puid, st->lx2nmi.intr, st->lx2nmi.flags); break; default: print("\t\n", st->type); } print("\n"); } enum{ FakeISA = 16 }; static void mknmi(int apicno, int intin, int flags) { PCMPintr intr; if(flags < 0) return; memset(&intr, 0, sizeof(intr)); intr.busno = FakeISA; /* our fake isa bus */ intr.intr = PcmpNMI; intr.flags = flags; intr.irq = 2; intr.apicno = apicno; intr.intin = intin; dprint("acpi lnmi apic %d in %d flags %#ux\n", apicno, intin, flags); mpnewlintr(&intr); } static void acpilapic(Apicst *a) { Apicst *l; Apic *apic; uint lint[2]; int flags; static int mpmachno; static void *va; lint[0] = lint[1] = -1; flags = PcmpEN; if(mpmachno == 0){ flags |= PcmpBP; va = vmap(apics->laddr, 1024); } if(va == nil){ print("can't map lapic\n"); return; } apic = mpnewapic(PcmpPROCESSOR, a->lapic.id, flags); if(apic == nil){ print("lapic already defined\n"); return; } apic->machno = mpmachno++; apic->paddr = apics->laddr; apic->addr = va; dprint("lapic %d at %#p l0\n", apic->apicno, apic->paddr); if(mpmachno == 1){ mpnewbus(BusPCI, 0); /* fake pci bus */ mpnewbus(BusISA, FakeISA); /* fake isa bus */ } for(l = apics->st; l != nil; l = l->next) if(l->type == ASlnmi && l->lnmi.pid == a->lapic.pid) if(l->lnmi.lint != 0 && l->lnmi.lint != 1) print("acpilapic: bad lint %d\n", l->lnmi.lint); else mknmi(apic->apicno, l->lnmi.lint, l->lnmi.flags); } /* * Must start the ioapic to learn its mre. * Needed to locate ioapics by acpi irq numbers. */ static void acpiioapic(Apicst *a) { Apic *apic; void *va; apic = mpnewapic(PcmpIOAPIC, a->ioapic.id, PcmpEN); if(apic == nil){ print("ioapic already defined"); return; } apic->paddr = a->ioapic.addr; apic->ibase = a->ioapic.ibase; if((va = vmap(apic->paddr, 1024)) == nil) apic->flags &= ~PcmpEN; else{ apic->addr = va; ioapicinit(apic, apic->apicno); } dprint("ioapic %d at %#p ibase %d\n", apic->apicno, apic->paddr, apic->ibase); } static Apic* getioapic(int airq) { int ibase; Apic *apic; for(apic = mpioapics; apic != nil; apic = apic->next){ ibase = apic->ibase; if(airq >= ibase && airq <= ibase+apic->mre) return apic; } return nil; } /* * add an isa interrupt entry. */ static void acpiint(int irq, int airq, int flags, int nmi) { PCMPintr intr; Apic *a; a = getioapic(airq); if(a == nil){ print("acpi: no ioapic for airq %d\n", airq); return; } if((flags&PcmpPOMASK) == 0) flags |= PcmpHIGH; if((flags&PcmpELMASK) == 0) flags |= PcmpEDGE; memset(&intr, 0, sizeof(intr)); intr.busno = FakeISA; /* our fake isa bus number */ intr.flags = flags; intr.intr = PcmpINT; if(nmi != 0) intr.intr = PcmpNMI; intr.irq = irq; intr.apicno = a->apicno; intr.intin = airq - a->ibase; dprint("acpi int: isa irq %d apic %d in %d flags %I\n", intr.irq, intr.apicno, intr.intin, intr.flags); mpnewintr(&intr); } static void acpiapics(void) { Apicst *l; int i, irq, isairqs; if(apics == nil) return; /* * Configure lapics. The first one is always the boot processor. */ for(l = apics->st; l != nil; l = l->next) switch(l->type){ case ASlapic: acpilapic(l); break; case ASioapic: acpiioapic(l); break; } /* * Add interrupt info now that we have configured all apics. * This means nmis and ISA irqs which, unless overriden, * are assumed to be identity mapped by ACPI. */ isairqs = 0; for(l = apics->st; l != nil; l = l->next) switch(l->type){ case ASintovr: irq = l->intovr.irq; if(irq >= 0 && irq < 16) isairqs |= 1<intovr.irq, l->intovr.intr, l->intovr.flags, 0); break; case ASnmi: acpiint(2, l->intovr.intr, l->intovr.flags, 1); break; } for(i = 0; i < 16; i++) if((isairqs & 1<laddr = l32get(p+36); apics->pcat = l32get(p+40); apics->st = nil; stl = &apics->st; pe = p + len; for(p += 44; p < pe; p += stlen){ st = mallocz(sizeof(Apicst), 1); st->type = p[0]; st->next = nil; stlen = p[1]; switch(st->type){ case ASlapic: st->lapic.pid = p[2]; st->lapic.id = p[3]; if(l32get(p+4) == 0){ free(st); st = nil; } break; case ASioapic: st->ioapic.id = p[2]; st->ioapic.addr = l32get(p+4); st->ioapic.ibase = l32get(p+8); break; case ASintovr: st->intovr.irq = p[3]; st->intovr.intr = l32get(p+4); st->intovr.flags = l16get(p+8); break; case ASnmi: st->nmi.flags = l16get(p+2); st->nmi.intr = l32get(p+4); break; case ASlnmi: st->lnmi.pid = p[2]; st->lnmi.flags = l16get(p+3); st->lnmi.lint = p[5]; break; case ASladdr: apics->laddr = l64get(p+8); break; case ASiosapic: id = st->iosapic.id = p[2]; st->iosapic.ibase = l32get(p+4); st->iosapic.addr = l64get(p+8); for(l = apics->st; l != nil; l = l->next) if(l->type == ASioapic && l->ioapic.id == id){ l->ioapic = st->iosapic; free(st); st = nil; break; } break; case ASlsapic: st->lsapic.pid = p[2]; st->lsapic.id = p[3]; st->lsapic.eid = p[4]; st->lsapic.puid = l32get(p+12); if(l32get(p+8) == 0){ free(st); st = nil; }else kstrdup(&st->lsapic.puids, (char*)p+16); break; case ASintsrc: st->intsrc.flags = l16get(p+2); st->type = p[4]; st->intsrc.pid = p[5]; st->intsrc.peid = p[6]; st->intsrc.iosv = p[7]; st->intsrc.intr = l32get(p+8); st->intsrc.any = l32get(p+12); break; case ASlx2apic: st->lx2apic.id = l32get(p+4); st->lx2apic.puid = l32get(p+12); if(l32get(p+8) == 0){ free(st); st = nil; } break; case ASlx2nmi: st->lx2nmi.flags = l16get(p+2); st->lx2nmi.puid = l32get(p+4); st->lx2nmi.intr = p[8]; break; default: print("acpi: unknown madt entry\n"); free(st); st = nil; } if(st != nil){ *stl = st; stl = &st->next; } } dumpmadt(apics); acpiapics(); } /* * Keep a copy of a table, for p9am(3). */ static void acpitable(uchar *p, int len) { Atable *t; if(len < Sdthdrsz){ print("acpi: short table: %d bytes\n", len); return; } t = newtable(p); t->p = malloc(len); if(t->p == nil) panic("acpitable: no memory"); memmove(t->p, p, len); } /* * Process the xsdt table and load tables found there. */ static int acpixsdtload(u8int* p, int len, int asize) { int i, l, t, found; uintptr dhpa; uchar *sdt; char tsig[5]; found = 0; for(i = 0; i < len; i += asize){ if(asize == 8) dhpa = l64get(p+i); else dhpa = l32get(p+i); if((sdt = sdtmap(dhpa, &l, 1)) == nil) continue; memmove(tsig, sdt, 4); tsig[4] = 0; dprint("acpi: %s addr %#p paddr %#p\n", tsig, sdt, dhpa); for(t = 0; t < nelem(ptables); t++) if(strcmp(tsig, ptables[t].sig) == 0){ ptables[t].f(sdt, l); break; } vunmap(sdt, l); } return found; } static void* rsdscan(u8int* addr, int len, char* signature) { int sl; u8int *e, *p; e = addr+len; sl = strlen(signature); for(p = addr; p+sl < e; p += 16){ if(memcmp(p, signature, sl)) continue; return p; } return nil; } static void* rsdsearch(char* signature) { uintptr p; u8int *bda; void *rsd; /* * Search for the data structure signature: * 1) in the first KB of the EBDA; * 2) in the BIOS ROM between 0xE0000 and 0xFFFFF. */ if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4) == 0){ bda = BIOSSEG(0x40); if((p = (bda[0x0F]<<8)|bda[0x0E])){ if(rsd = rsdscan(KADDR(p), 1024, signature)) return rsd; } } return rsdscan(BIOSSEG(0xE000), 0x20000, signature); } static uint bankedin(uintptr ra, uintptr rb, int sz) { uint r; r = 0; switch(sz){ case 1: if(ra != 0) r |= inb(ra); if(rb != 0) r |= inb(rb); break; case 2: if(ra != 0) r |= ins(ra); if(rb != 0) r |= ins(rb); break; case 4: if(ra != 0) r |= inl(ra); if(rb != 0) r |= inl(rb); break; default: print("bankedin: wrong size\n"); } return r; } static uint bankedout(uintptr ra, uintptr rb, int sz, int v) { uint r; r = -1; switch(sz){ case 1: if(ra != 0) outb(ra, v); if(rb != 0) outb(rb, v); break; case 2: if(ra != 0) outs(ra, v); if(rb != 0) outs(rb, v); break; case 4: if(ra != 0) outl(ra, v); if(rb != 0) outl(rb, v); break; default: print("bankedout: wrong size\n"); } return r; } static uint getpm1ctl(void) { return bankedin(fadt.pm1acntblk, fadt.pm1bcntblk, fadt.pm1cntlen); } static void setpm1sts(uint v) { bankedout(fadt.pm1aevtblk, fadt.pm1bevtblk, fadt.pm1evtlen/2, v); } static uint getpm1sts(void) { return bankedin(fadt.pm1aevtblk, fadt.pm1bevtblk, fadt.pm1evtlen/2); } static uint getpm1en(void) { int sz; sz = fadt.pm1evtlen/2; return bankedin(fadt.pm1aevtblk+sz, fadt.pm1bevtblk+sz, sz); } static int getgpeen(int n) { return inb(gpes[n].enio) & 1<= ngpes) error("gpe out of range"); kstrdup(&gpes[i].obj, obj); dprint("acpi: gpe %d %s\n", i, gpes[i].obj); setgpeen(i, 1); } void acpipwr(int i, int pm1a, int pm1b) { if(i >= Npwr) error("bad power state number"); slptyp[i].pm1a = pm1a; slptyp[i].pm1b = pm1b; dprint("acpi: pwr %d pm1a %#ux pm1b %#ux\n", i, slptyp[i].pm1a, slptyp[i].pm1b); } /* * ACPI interrupt routine. Something happen to the fixed * hardware or we must notify an event to 9am. */ static void acpiintr(Ureg*, void*) { int i; uint sts, en; // print("acpi: intr\n"); for(i = 0; i < ngpes; i++) if(getgpests(i)){ print("gpe %d on\n", i); en = getgpeen(i); setgpeen(i, 0); clrgpests(i); if(en != 0) print("acpiitr: calling gpe %d\n", i); // queue gpe for calling gpe->ho in the // aml process. // enable it again when it returns. } sts = getpm1sts(); en = getpm1en(); // print("acpiitr: pm1sts %#ux pm1en %#ux\n", sts, en); // if(sts&en) // print("have enabled events\n"); // if(sts&1) // print("power button\n"); // XXX serve other interrupts here. setpm1sts(sts); } static void initgpes(void) { int i, n0, n1; n0 = fadt.gpe0blklen/2; n1 = fadt.gpe1blklen/2; ngpes = n0 + n1; gpes = mallocz(sizeof(Gpe) * ngpes, 1); for(i = 0; i < n0; i++){ gpes[i].nb = i; gpes[i].stsbit = i&7; gpes[i].stsio = fadt.gpe0blk + i>>3; gpes[i].enbit = (n0 + i)&7; gpes[i].enio = fadt.gpe0blk + ((n0 + i)>>3); } for(i = 0; i + n0 < ngpes; i++){ gpes[i + n0].nb = fadt.gp1base + i; gpes[i + n0].stsbit = i&7; gpes[i + n0].stsio = fadt.gpe1blk + i>>3; gpes[i + n0].enbit = (n1 + i)&7; gpes[i + n0].enio = fadt.gpe1blk + ((n1 + i)>>3); } for(i = 0; i < ngpes; i++){ setgpeen(i, 0); clrgpests(i); } } static void acpiioalloc(uint addr, int len) { if(addr != 0) ioalloc(addr, len, 0, "acpi"); } static void acpirsdptr(void) { Rsdp *rsd; int asize; uintptr sdtpa; u8int *p; int len; if((rsd = rsdsearch("RSD PTR ")) == nil){ dprint("no rsd ptr\n"); return; } assert(sizeof(Sdthdr) == Sdthdrsz); dprint("acpi: RSD PTR @ %#p, physaddr %#ux length %ud %#llux rev %d\n", rsd, l32get(rsd->raddr), l32get(rsd->length), l64get(rsd->xaddr), rsd->revision); if(rsd->revision >= 2){ if(sdtchecksum(rsd, 36) == nil){ dprint("acpi: RSD: bad checksum\n"); return; } sdtpa = l64get(rsd->xaddr); asize = 8; } else{ if(sdtchecksum(rsd, 20) == nil){ dprint("acpi: RSD: bad checksum\n"); return; } sdtpa = l32get(rsd->raddr); asize = 4; } /* * process the RSDT or XSDT table. */ if((p = sdtmap(sdtpa, &len, 1)) == nil) return; if((p[0] == 'R' || p[0] == 'X') && memcmp(p+1, "SDT", 3) == 0){ dprint("acpi: %cSDT %#p\n", p[0], p); acpixsdtload(p + Sdthdrsz, len - Sdthdrsz, asize); } vunmap(p, len); } void acpistart(void) { if(0) if(fadt.sciint != 0) intrenable(fadt.sciint, acpiintr, 0, BUSUNKNOWN, "acpi"); } void acpiinit(void) { int i; char *p; static int initialized; if((p = getconf("*noacpi")) != nil && strtol(p, 0, 0) != 0) return; if(initialized++ > 0) return; fmtinstall('G', Gfmt); fmtinstall('R', Rfmt); fmtinstall('I', Ifmt); acpirsdptr(); if(fadt.smicmd == 0) return; /* stop if we want just acpi tables; but no aml */ if((p = getconf("*9am")) == nil || strtol(p, 0, 0) == 0) return; /* * should use fadt->xpm* and fadt->xgpe* registers for 64 bits. */ dprint("acpi io alloc\n"); acpiioalloc(fadt.smicmd, 1); acpiioalloc(fadt.pm1aevtblk, fadt.pm1evtlen); acpiioalloc(fadt.pm1bevtblk, fadt.pm1evtlen ); acpiioalloc(fadt.pm1acntblk, fadt.pm1cntlen); acpiioalloc(fadt.pm1bcntblk, fadt.pm1cntlen); acpiioalloc(fadt.pm2cntblk, fadt.pm2cntlen); acpiioalloc(fadt.pmtmrblk, fadt.pmtmrlen); acpiioalloc(fadt.gpe0blk, fadt.gpe0blklen); acpiioalloc(fadt.gpe1blk, fadt.gpe1blklen); dprint("acpi init gpes\n"); initgpes(); /* * This starts ACPI, which may require we handle * events ourselves. Use with care. */ dprint("enabling acpi..."); if((getpm1ctl() & Pm1SciEn) != 0){ dprint("always enabled\n"); return; } outb(fadt.smicmd, fadt.acpienable); for(i = 0; i < 10; i++) if(getpm1ctl() & Pm1SciEn) break; if(i == 10){ dprint("failed to enable\n"); outb(fadt.smicmd, fadt.acpidisable); fadt.smicmd = 0; }else dprint("\n"); } static int gasio(Gas *g, void *p, int len) { Reg r; if(g->len == 0) return -1; r.name = "gas"; r.spc = g->spc; if(r.spc != Rsysmem && r.spc != Rsysio){ print("bug: gasio:space %#ux not implemented\n", r.spc); /* should at least support pci for reset */ return -1; } r.len = g->len/8; r.base = g->addr; r.accsz = g->accsz/8; if(r.accsz == 0) r.accsz = 1; return acpiregio(&r, p, len, g->off, 1); } void acpireset(void) { gasio(&fadt.resetreg, &fadt.resetval, fadt.resetreg.len); } 9am/9pc/acpi.h 664 0 0 10441 11334032360 11145ustar00nemosys/* * ACPI definitions in a distilled form * for device drivers and resources. */ typedef struct ACPIdev ACPIdev; typedef struct ACPIres ACPIres; typedef struct ACPIaddr ACPIaddr; typedef struct ACPIlapic ACPIlapic; typedef struct ACPIioapic ACPIioapic; typedef struct ACPIint ACPIint; typedef struct Reg Reg; enum { /* resource types known to us; keep this order */ Rirq = 0, Rdma, Rio, Rmem, Rmemas, /* mem address space */ Rioas, /* io address space */ Rbusas, /* bus address space */ Rpir, /* pci interrupt routing info */ Rbbn, /* base bus number */ /* ACPI dma resource flags */ Dmachanmask = 3<<5, Dmachancompat = 0<<5, Dmachana = 1<<5, Dmachanb = 2<<5, Dmachanf = 3<<5, Dmabm = 1<<1, Dmaszmask = 3, Dma8 = 0, Dma8and16 = 1, Dma16 = 2, /* address space resource flags */ /* type specific flags are kept in the lo byte */ Asmaxfixed = 1<<(3+8), Asminfixed = 1<<(2+8), Asconsume = 1<<(0+8), /* consumes or produces this as? */ /* ACPI (extended) address space attrs */ Muc = 1, Mwc = 2, Mwt = 4, Mwb = 8, Muce = 0x10, /* uncached exported, semaphores */ Mnvr = 0x8000, /* ACPI regions. Gas ids */ Rsysmem = 0, Rsysio, Rpcicfg, Rembed, Rsmbus, Rcmos, Rpcibar, Ripmi, Rfixedhw = 0x7f, }; /* * Information about a device. */ struct ACPIdev { ACPIdev* next; /* next device in flat device list */ ACPIdev* child; /* inner devices for buses */ ACPIdev* cnext; /* next sibling */ char* name; /* device name */ char* acpiname; /* path in the acpi ns */ char* pnpid; /* pnp id */ u64int uid; /* unique id valid within same pnpid */ u64int addr; /* address, see below. */ ACPIres* res; /* current resource settings */ ACPIres* other; /* other possible res. settings */ }; /* * A resource related to a device. */ struct ACPIres { int type; ACPIres* next; /* in device resoure list */ union{ struct{ u32int nb[16]; uint flags; /* like in MPS */ }irq; struct{ uint chans; uint flags; }dma; struct{ u64int min; /* address of the range */ u64int max; /* address of the range */ uint align; /* 1, 2, 4, 8 */ int isrw; /* memory only: ro or rw? */ }io, mem; struct{ int flags; /* min, max are fixed? */ u64int mask; /* which bits are decoded */ u64int min; /* address on the other side */ u64int max; /* idem. */ u64int len; /* could be < max-min+1, if not fixed */ long off; /* translation adds this */ u64int attr; /* resource specific flags */ }as; struct{ int dno; /* pci device number. */ int pin; /* interrupt pin 0-3 */ int link; /* -1 or 0=LNKA, ... */ int airq; /* acpi interrupt number */ int apic; /* io apic id (if no link) */ int inti; /* io apic input (if no link) */ }pir; int bbn; /* base bus number */ }; }; #pragma varargck type "R" ACPIres* /* * ACPI region; used by the implementation. */ struct Reg { char* name; int spc; /* io space */ u64int base; /* address, physical */ uchar* p; /* address, kmapped */ long plen; /* length kmapped */ u64int len; int tbdf; int accsz; /* access size */ }; /* * ACPI addresses are encoded in u64int. Meaning depends * on the device type: * [E]ISA: slot nb. * Floppy: drive select for int13. * IDEctlr: 0:primary; 1:secondary * IDEchan: 0:master; 1:slave * PCI: 0xDDDDFFFF; device and function. * PCMCIA: slot nb. * SATA: 0xPPPPMMMM; port number and 0xFFFF or multipler port. * USB: port number */ #define ACPIpcidev(a) ((ulong)((a)>>16 & 0xFFFF)) #define ACPIpcifn(a) ((ulong)((a) & 0xFFFF)) #define ACPIsataport(a) ((ulong)((a)>>16 & 0xFFFF)) #define ACPIsataportmult(a) ((ulong)((a)&0xFFFF)) /* * Primary interface for configuration: * Flat list of devices through ACPIdev.next. * Child device lists through ACPIdev.child and ACPIdev.cnext; */ extern ACPIdev *acpidevs; /* List of devices */ extern int legacydevices; /* Has ISA devices */ extern int keyboardpresent; /* i8042 is present */ extern int novgapresent; /* do not probe for vga memory */ /* Used by the acpi implementation */ extern char* acpiregstr(int id); extern long acpiregio(Reg *r, void *p, ulong len, uintptr off, int iswr); extern void acpigpe(int i, char *obj); extern void acpiinit(void); extern void acpipwr(int i, int pm1a, int pm1b); extern void acpireset(void); extern void acpistart(void); extern char* smprinttables(void); old = inb(gpes[n].enio); if(v) outb(gpes[n].enio, old | 1<machno); lapicw(LapicTICR, lapictimer.max); lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER)); lapicw(LapicTPR, 0); } /* * use the i8253 clock to figure out our lapic timer rate. */ static void lapictimerinit(void) { uvlong x, v, hz; v = m->cpuhz/1000; lapicw(LapicTDCR, LapicX1); lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER)); if(lapictimer.hz == 0ULL){ x = fastticks(&hz); x += hz/10; lapicw(LapicTICR, 0xffffffff); do{ v = fastticks(nil); }while(v < x); lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10; lapictimer.max = lapictimer.hz/HZ; lapictimer.min = lapictimer.hz/(100*HZ); if(lapictimer.hz > hz-(hz/10)){ if(lapictimer.hz > hz+(hz/10)) iprint("lapic clock %lld > cpu clock > %lld\n", lapictimer.hz, hz); lapictimer.hz = hz; } lapictimer.div = hz/lapictimer.hz; } } void lapicinit(Apic* apic) { ulong r, lvt; if(lapicbase == 0) lapicbase = apic->addr; lapicw(LapicDFR, 0xFFFFFFFF); r = (lapicr(LapicID)>>24) & 0xFF; lapicw(LapicLDR, (1<cpuidax & 0xFFF){ case 0x526: /* stepping cB1 */ case 0x52B: /* stepping E0 */ case 0x52C: /* stepping cC0 */ wrmsr(0x0E, 1<<14); /* TR12 */ break; } /* * Set the local interrupts. It's likely these should just be * masked off for SMP mode as some Pentium Pros have problems if * LINT[01] are set to ExtINT. * Acknowledge any outstanding interrupts. lapicw(LapicLINT0, apic->lintr[0]); lapicw(LapicLINT1, apic->lintr[1]); */ lapiceoi(0); lvt = (lapicr(LapicVER)>>16) & 0xFF; if(lvt >= 4) lapicw(LapicPCINT, ApicIMASK); lapicw(LapicERROR, VectorPIC+IrqERROR); lapicw(LapicESR, 0); lapicr(LapicESR); /* * Issue an INIT Level De-Assert to synchronise arbitration ID's. */ lapicw(LapicICRHI, 0); lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT); while(lapicr(LapicICRLO) & ApicDELIVS) ; /* * Do not allow acceptance of interrupts until all initialisation * for this processor is done. For the bootstrap processor this can be * early duing initialisation. For the application processors this should * be after the bootstrap processor has lowered priority and is accepting * interrupts. lapicw(LapicTPR, 0); */ } void lapicstartap(Apic* apic, int v) { int i; ulong crhi; crhi = apic->apicno<<24; lapicw(LapicICRHI, crhi); lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT); microdelay(200); lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT); delay(10); for(i = 0; i < 2; i++){ lapicw(LapicICRHI, crhi); lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG)); microdelay(200); } } void lapicerror(Ureg*, void*) { ulong esr; lapicw(LapicESR, 0); esr = lapicr(LapicESR); switch(m->cpuidax & 0xFFF){ case 0x526: /* stepping cB1 */ case 0x52B: /* stepping E0 */ case 0x52C: /* stepping cC0 */ return; } print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr); } void lapicspurious(Ureg*, void*) { print("cpu%d: lapicspurious\n", m->machno); } int lapicisr(int v) { ulong isr; isr = lapicr(LapicISR + (v/32)); return isr & (1<<(v%32)); } int lapiceoi(int v) { lapicw(LapicEOI, 0); return v; } void lapicicrw(ulong hi, ulong lo) { lapicw(LapicICRHI, hi); lapicw(LapicICRLO, lo); } void ioapicrdtr(Apic* apic, int sel, int* hi, int* lo) { ulong *iowin; iowin = apic->addr+(0x10/sizeof(ulong)); sel = IoapicRDT + 2*sel; lock(apic); *apic->addr = sel+1; if(hi) *hi = *iowin; *apic->addr = sel; if(lo) *lo = *iowin; unlock(apic); } void ioapicrdtw(Apic* apic, int sel, int hi, int lo) { ulong *iowin; iowin = apic->addr+(0x10/sizeof(ulong)); sel = IoapicRDT + 2*sel; lock(apic); *apic->addr = sel+1; *iowin = hi; *apic->addr = sel; *iowin = lo; unlock(apic); } void ioapicinit(Apic* apic, int apicno) { int hi, lo, v; ulong *iowin; /* * Initialise the I/O APIC. * The MultiProcessor Specification says it is the responsibility * of the O/S to set the APIC id. * Make sure interrupts are all masked off for now. */ iowin = apic->addr+(0x10/sizeof(ulong)); lock(apic); *apic->addr = IoapicVER; apic->mre = (*iowin>>16) & 0xFF; *apic->addr = IoapicID; *iowin = apicno<<24; unlock(apic); hi = 0; lo = ApicIMASK; for(v = 0; v <= apic->mre; v++) ioapicrdtw(apic, v, hi, lo); } void lapictimerset(uvlong next) { vlong period; int x; x = splhi(); lock(&m->apictimerlock); period = lapictimer.max; if(next != 0){ period = next - fastticks(nil); period /= lapictimer.div; if(period < lapictimer.min) period = lapictimer.min; else if(period > lapictimer.max - lapictimer.min) period = lapictimer.max; } lapicw(LapicTICR, period); unlock(&m->apictimerlock); splx(x); } void lapicclock(Ureg *u, void*) { /* * since the MTRR updates need to be synchronized across processors, * we want to do this within the clock tick. */ mtrrclock(); timerintr(u, 0); } void lapicintron(void) { lapicw(LapicTPR, 0); } void lapicintroff(void) { lapicw(LapicTPR, 0xFF); } void lapicnmienable(void) { lapicw(LapicPCINT, ApicNMI); } void lapicnmidisable(void) { lapicw(LapicPCINT, ApicIMASK); } e for vga memory */ /* Used by the acpi implementation */ extern char* acpiregstr(i9am/9pc/archacpi.c 664 0 0 2312 11334032360 11754ustar00nemosys/* * ACPI based MP arch support. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "mp.h" #include "acpi.h" #define dprint if(debug)print static int debug = 1; static int aidentify(void); static void ainit(void); PCArch archacpi = { .id= "acpi", .ident= aidentify, .reset= mpshutdown, .intrinit= ainit, .intrenable= mpintrenable, .intron= lapicintron, .introff= lapicintroff, .fastclock= i8253read, .timerset= lapictimerset, }; static int aidentify(void) { char *p; /* * Too many options. */ if((p = getconf("*noacpi")) != nil && strtol(p, 0, 0) != 0) return 1; if((p = getconf("*nomp")) != nil && strtol(p, 0, 0) != 0) return 1; if((p = getconf("*9am")) == nil || strtol(p, 0, 0) == 0) return 1; if(cpuserver && m->havetsc) archacpi.fastclock = tscticks; /* * We don't know if ACPI will be ok before having * user processes. So say yes; time will tell. * Could check out here if we have rsd ptr. * But that belongs to acpi.c */ return 0; } static void ainit(void) { Apic *bpapic; i8259init(); syncclock(); bpapic = mplapics; if(bpapic != nil) mpstart(bpapic); else dprint("archacpi: no lapics\n"); } 280, /* Error Status */ LapicICRLO = 0x0300, /* Interrupt Command */ LapicICRHI = 0x0310, /* Interrupt Command [63:32] */ LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */ LapicPCINT = 0x0340, /* Performance Counter LVT */ LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */ LapicLINT1 = 0x03609am/9pc/archmp.c 664 0 0 12462 11334032360 11503ustar00nemosys#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "mp.h" static _MP_ *_mp_; static PCMP* mppcmp; static int mpmachno = 1; static _MP_* mpscan(uchar *addr, int len) { uchar *e, *p, sum; int i; e = addr+len; for(p = addr; p < e; p += sizeof(_MP_)){ if(memcmp(p, "_MP_", 4)) continue; sum = 0; for(i = 0; i < sizeof(_MP_); i++) sum += p[i]; if(sum == 0) return (_MP_*)p; } return 0; } static _MP_* mpsearch(void) { uchar *bda; ulong p; _MP_ *mp; /* * Search for the MP Floating Pointer Structure: * 1) in the first KB of the EBDA; * 2) in the last KB of system base memory; * 3) in the BIOS ROM between 0xE0000 and 0xFFFFF. */ bda = KADDR(0x400); if((p = (bda[0x0F]<<8)|bda[0x0E])){ if(mp = mpscan(KADDR(p<<4), 1024)) return mp; } else{ p = ((bda[0x14]<<8)|bda[0x13])*1024; if(mp = mpscan(KADDR(p-1024), 1024)) return mp; } return mpscan(KADDR(0xF0000), 0x10000); } static void mpinit(void); static int identify(void); PCArch archmp = { .id= "_MP_", .ident= identify, .reset= mpshutdown, .intrinit= mpinit, .intrenable= mpintrenable, .intron= lapicintron, .introff= lapicintroff, .fastclock= i8253read, .timerset= lapictimerset, }; static int identify(void) { char *cp; PCMP *pcmp; uchar *p, sum; ulong length; if((cp = getconf("*nomp")) != nil && strtol(cp, 0, 0) != 0) return 1; /* * Search for an MP configuration table. For now, * don't accept the default configurations (physaddr == 0). * Check for correct signature, calculate the checksum and, * if correct, check the version. * To do: check extended table checksum. */ if((_mp_ = mpsearch()) == 0 || _mp_->physaddr == 0) return 1; pcmp = KADDR(_mp_->physaddr); if(memcmp(pcmp, "PCMP", 4)) return 1; length = pcmp->length; sum = 0; for(p = (uchar*)pcmp; length; length--) sum += *p++; if(sum || (pcmp->version != 1 && pcmp->version != 4)) return 1; if(cpuserver && m->havetsc) archmp.fastclock = tscticks; return 0; } static char* buses[] = { "CBUSI ", "CBUSII", "EISA ", "FUTURE", "INTERN", "ISA ", "MBI ", "MBII ", "MCA ", "MPI ", "MPSA ", "NUBUS ", "PCI ", "PCMCIA", "TC ", "VL ", "VME ", "XPRESS", 0, }; static Apic* mkprocessor(PCMPprocessor* p) { Apic *apic; if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) return nil; apic = mpnewapic(PcmpPROCESSOR, p->apicno, p->flags); apic->lintr[0] = ApicIMASK; apic->lintr[1] = ApicIMASK; if(p->flags & PcmpBP) apic->machno = 0; else{ apic->machno = mpmachno; mpmachno++; } return apic; } static Bus* mkbus(PCMPbus* p) { int i; for(i = 0; buses[i]; i++) if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) return mpnewbus(i, p->busno); return nil; } static Apic* mkioapic(PCMPioapic* p) { Apic *apic; void *va; if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) return nil; /* * Map the I/O APIC. */ if((va = vmap(p->addr, 1024)) == nil) return 0; apic = mpnewapic(PcmpIOAPIC, p->apicno, p->flags); apic->addr = va; apic->paddr = p->addr; return apic; } static void fixx38mlstintr(Aintr *aintr) { PCMPintr *p; p = aintr->intr; /* * Hack for Intel SR1520ML motherboard, which BIOS describes * the i82575 dual ethernet controllers incorrectly. */ if(memcmp(mppcmp->product, "INTEL X38MLST ", 20) == 0) if(p->busno == 1 && p->intin == 16 && p->irq == 1){ print("mkiointr: %20.20s bus %d intin %d irq %d\n", (char*)mppcmp->product, p->busno, p->intin, p->irq); p->intin = 17; } } static void mpinit(void) { PCMP *pcmp; uchar *e, *p; Apic *apic, *bpapic; void *va; Aintr *aintr; i8259init(); syncclock(); if(_mp_ == 0) return; pcmp = KADDR(_mp_->physaddr); /* * Map the local APIC. */ if((va = vmap(pcmp->lapicbase, 1024)) == nil) return; mppcmp = pcmp; print("LAPIC: %.8lux %.8lux\n", pcmp->lapicbase, (ulong)va); bpapic = nil; /* * Run through the table saving information needed for starting * application processors and initialising any I/O APICs. The table * is guaranteed to be in order such that only one pass is necessary. */ p = ((uchar*)pcmp)+sizeof(PCMP); e = ((uchar*)pcmp)+pcmp->length; while(p < e) switch(*p){ default: print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", *p, e-p); while(p < e){ print("%uX ", *p); p++; } break; case PcmpPROCESSOR: if(apic = mkprocessor((PCMPprocessor*)p)){ /* * Must take a note of bootstrap processor APIC * now as it will be needed in order to start the * application processors later and there's no * guarantee that the bootstrap processor appears * first in the table before the others. */ apic->addr = va; apic->paddr = pcmp->lapicbase; if(apic->flags & PcmpBP) bpapic = apic; } p += sizeof(PCMPprocessor); continue; case PcmpBUS: mkbus((PCMPbus*)p); p += sizeof(PCMPbus); continue; case PcmpIOAPIC: if(apic = mkioapic((PCMPioapic*)p)) ioapicinit(apic, ((PCMPioapic*)p)->apicno); p += sizeof(PCMPioapic); continue; case PcmpIOINTR: aintr = mpnewintr((PCMPintr*)p); fixx38mlstintr(aintr); p += sizeof(PCMPintr); continue; case PcmpLINTR: mpnewlintr((PCMPintr*)p); p += sizeof(PCMPintr); continue; } /* * No bootstrap processor, no need to go further. */ if(bpapic == 0) return; mpstart(bpapic); } cpu%d: lapicspurious\n", m->machno); } int lapicisr(int v) { ulong isr; isr = lapicr(LapicISR + (v/32)); return isr & (1<<(v%32)); } int lapiceoi(int v) { lapicw(LapicEOI, 0); return v; } void la9am/9pc/devarch.c 664 0 0 51471 11334032360 11650ustar00nemosys#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" typedef struct IOMap IOMap; struct IOMap { IOMap *next; int reserved; char tag[13]; ulong start; ulong end; }; static struct { Lock; IOMap *m; IOMap *free; IOMap maps[32]; /* some initial free maps */ QLock ql; /* lock for reading map */ } iomap; enum { Qdir = 0, Qioalloc = 1, Qiob, Qiow, Qiol, Qbase, Qmax = 16, }; enum { /* cpuid standard function codes */ Highstdfunc = 0, /* also returns vendor string */ Procsig, Proctlbcache, Procserial, }; typedef long Rdwrfn(Chan*, void*, long, vlong); static Rdwrfn *readfn[Qmax]; static Rdwrfn *writefn[Qmax]; static Dirtab archdir[Qmax] = { ".", { Qdir, 0, QTDIR }, 0, 0555, "ioalloc", { Qioalloc, 0 }, 0, 0444, "iob", { Qiob, 0 }, 0, 0660, "iow", { Qiow, 0 }, 0, 0660, "iol", { Qiol, 0 }, 0, 0660, }; Lock archwlock; /* the lock is only for changing archdir */ int narchdir = Qbase; int (*_pcmspecial)(char*, ISAConf*); void (*_pcmspecialclose)(int); static int doi8253set = 1; /* * Add a file to the #P listing. Once added, you can't delete it. * You can't add a file with the same name as one already there, * and you get a pointer to the Dirtab entry so you can do things * like change the Qid version. Changing the Qid path is disallowed. */ Dirtab* addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn) { int i; Dirtab d; Dirtab *dp; memset(&d, 0, sizeof d); strcpy(d.name, name); d.perm = perm; lock(&archwlock); if(narchdir >= Qmax){ unlock(&archwlock); return nil; } for(i=0; inext){ m = *l; if (m->start < 0x400) continue; i = m->start - port; if(i > size) break; if(align > 0) port = ((port+align-1)/align)*align; else port = m->end; } if(*l == nil){ unlock(&iomap); return -1; } m = iomap.free; if(m == nil){ print("ioalloc: out of maps"); unlock(&iomap); return port; } iomap.free = m->next; m->next = *l; m->start = port; m->end = port + size; m->reserved = 1; strncpy(m->tag, tag, sizeof(m->tag)); m->tag[sizeof(m->tag)-1] = 0; *l = m; archdir[0].qid.vers++; unlock(&iomap); return m->start; } /* * alloc some io port space and remember who it was * alloced to. if port < 0, find a free region. */ int ioalloc(int port, int size, int align, char *tag) { IOMap *m, **l; int i; lock(&iomap); if(port < 0){ /* find a free port above 0x400 and below 0x1000 */ port = 0x400; for(l = &iomap.m; *l; l = &(*l)->next){ m = *l; if (m->start < 0x400) continue; i = m->start - port; if(i > size) break; if(align > 0) port = ((port+align-1)/align)*align; else port = m->end; } if(*l == nil){ unlock(&iomap); return -1; } } else { /* Only 64KB I/O space on the x86. */ if((port+size) > 0x10000){ unlock(&iomap); return -1; } /* see if the space clashes with previously allocated ports */ for(l = &iomap.m; *l; l = &(*l)->next){ m = *l; if(m->end <= port) continue; if(m->reserved && m->start == port && m->end == port + size) { m->reserved = 0; unlock(&iomap); return m->start; } if(m->start >= port+size) break; unlock(&iomap); return -1; } } m = iomap.free; if(m == nil){ print("ioalloc: out of maps"); unlock(&iomap); return port; } iomap.free = m->next; m->next = *l; m->start = port; m->end = port + size; strncpy(m->tag, tag, sizeof(m->tag)); m->tag[sizeof(m->tag)-1] = 0; *l = m; archdir[0].qid.vers++; unlock(&iomap); return m->start; } void iofree(int port) { IOMap *m, **l; lock(&iomap); for(l = &iomap.m; *l; l = &(*l)->next){ if((*l)->start == port){ m = *l; *l = m->next; m->next = iomap.free; iomap.free = m; break; } if((*l)->start > port) break; } archdir[0].qid.vers++; unlock(&iomap); } int iounused(int start, int end) { IOMap *m; for(m = iomap.m; m; m = m->next){ if(start >= m->start && start < m->end || start <= m->start && end > m->start) return 0; } return 1; } static void checkport(int start, int end) { /* standard vga regs are OK */ if(start >= 0x2b0 && end <= 0x2df+1) return; if(start >= 0x3c0 && end <= 0x3da+1) return; if(iounused(start, end)) return; error(Eperm); } static Chan* archattach(char* spec) { return devattach('P', spec); } Walkqid* archwalk(Chan* c, Chan *nc, char** name, int nname) { return devwalk(c, nc, name, nname, archdir, narchdir, devgen); } static int archstat(Chan* c, uchar* dp, int n) { return devstat(c, dp, n, archdir, narchdir, devgen); } static Chan* archopen(Chan* c, int omode) { return devopen(c, omode, archdir, narchdir, devgen); } static void archclose(Chan*) { } enum { Linelen= 31, }; static long archread(Chan *c, void *a, long n, vlong offset) { char *buf, *p; int port; ushort *sp; ulong *lp; IOMap *m; Rdwrfn *fn; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, a, n, archdir, narchdir, devgen); case Qiob: port = offset; checkport(offset, offset+n); for(p = a; port < offset+n; port++) *p++ = inb(port); return n; case Qiow: if(n & 1) error(Ebadarg); checkport(offset, offset+n); sp = a; for(port = offset; port < offset+n; port += 2) *sp++ = ins(port); return n; case Qiol: if(n & 3) error(Ebadarg); checkport(offset, offset+n); lp = a; for(port = offset; port < offset+n; port += 4) *lp++ = inl(port); return n; case Qioalloc: break; default: if(c->qid.path < narchdir && (fn = readfn[c->qid.path])) return fn(c, a, n, offset); error(Eperm); break; } if((buf = malloc(n)) == nil) error(Enomem); p = buf; n = n/Linelen; offset = offset/Linelen; lock(&iomap); for(m = iomap.m; n > 0 && m != nil; m = m->next){ if(offset-- > 0) continue; sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag); p += Linelen; n--; } unlock(&iomap); n = p - buf; memmove(a, buf, n); free(buf); return n; } static long archwrite(Chan *c, void *a, long n, vlong offset) { char *p; int port; ushort *sp; ulong *lp; Rdwrfn *fn; switch((ulong)c->qid.path){ case Qiob: p = a; checkport(offset, offset+n); for(port = offset; port < offset+n; port++) outb(port, *p++); return n; case Qiow: if(n & 1) error(Ebadarg); checkport(offset, offset+n); sp = a; for(port = offset; port < offset+n; port += 2) outs(port, *sp++); return n; case Qiol: if(n & 3) error(Ebadarg); checkport(offset, offset+n); lp = a; for(port = offset; port < offset+n; port += 4) outl(port, *lp++); return n; default: if(c->qid.path < narchdir && (fn = writefn[c->qid.path])) return fn(c, a, n, offset); error(Eperm); break; } return 0; } Dev archdevtab = { 'P', "arch", devreset, devinit, devshutdown, archattach, archwalk, archstat, archopen, devcreate, archclose, archread, devbread, archwrite, devbwrite, devremove, devwstat, }; /* * the following is a generic version of the * architecture specific stuff */ static int unimplemented(int) { return 0; } static void nop(void) { } static void archreset(void) { i8042reset(); /* * Often the BIOS hangs during restart if a conventional 8042 * warm-boot sequence is tried. The following is Intel specific and * seems to perform a cold-boot, but at least it comes back. * And sometimes there is no keyboard... * * The reset register (0xcf9) is usually in one of the bridge * chips. The actual location and sequence could be extracted from * ACPI but why bother, this is the end of the line anyway. */ print("Takes a licking and keeps on ticking...\n"); *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */ outb(0xcf9, 0x02); outb(0xcf9, 0x06); for(;;) idle(); } /* * 386 has no compare-and-swap instruction. * Run it with interrupts turned off instead. */ static int cmpswap386(long *addr, long old, long new) { int r, s; s = splhi(); if(r = (*addr == old)) *addr = new; splx(s); return r; } /* * On a uniprocessor, you'd think that coherence could be nop, * but it can't. We still need a barrier when using coherence() in * device drivers. * * On VMware, it's safe (and a huge win) to set this to nop. * Aux/vmware does this via the #P/archctl file. */ void (*coherence)(void) = nop; int (*cmpswap)(long*, long, long) = cmpswap386; PCArch* arch; extern PCArch* knownarch[]; PCArch archgeneric = { .id= "generic", .ident= 0, .reset= archreset, .serialpower= unimplemented, .modempower= unimplemented, .intrinit= i8259init, .intrenable= i8259enable, .intrvecno= i8259vecno, .intrdisable= i8259disable, .intron= i8259on, .introff= i8259off, .clockenable= i8253enable, .fastclock= i8253read, .timerset= i8253timerset, }; typedef struct X86type X86type; struct X86type { int family; int model; int aalcycles; char* name; }; static X86type x86intel[] = { { 4, 0, 22, "486DX", }, /* known chips */ { 4, 1, 22, "486DX50", }, { 4, 2, 22, "486SX", }, { 4, 3, 22, "486DX2", }, { 4, 4, 22, "486SL", }, { 4, 5, 22, "486SX2", }, { 4, 7, 22, "DX2WB", }, /* P24D */ { 4, 8, 22, "DX4", }, /* P24C */ { 4, 9, 22, "DX4WB", }, /* P24CT */ { 5, 0, 23, "P5", }, { 5, 1, 23, "P5", }, { 5, 2, 23, "P54C", }, { 5, 3, 23, "P24T", }, { 5, 4, 23, "P55C MMX", }, { 5, 7, 23, "P54C VRT", }, { 6, 1, 16, "PentiumPro", },/* trial and error */ { 6, 3, 16, "PentiumII", }, { 6, 5, 16, "PentiumII/Xeon", }, { 6, 6, 16, "Celeron", }, { 6, 7, 16, "PentiumIII/Xeon", }, { 6, 8, 16, "PentiumIII/Xeon", }, { 6, 0xB, 16, "PentiumIII/Xeon", }, { 6, 0xF, 16, "Xeon5000-series", }, { 6, 0x16, 16, "Core 2 (Intel 64)", }, { 6, 0x17, 16, "Core 2 (Intel 64)", }, { 6, 0x1c, 16, "Atom", }, { 0xF, 1, 16, "P4", }, /* P4 */ { 0xF, 2, 16, "PentiumIV/Xeon", }, { 3, -1, 32, "386", }, /* family defaults */ { 4, -1, 22, "486", }, { 5, -1, 23, "P5", }, { 6, -1, 16, "P6", }, { 0xF, -1, 16, "P4", }, /* P4 */ { -1, -1, 16, "unknown", }, /* total default */ }; /* * The AMD processors all implement the CPUID instruction. * The later ones also return the processor name via functions * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX * and DX: * K5 "AMD-K5(tm) Processor" * K6 "AMD-K6tm w/ multimedia extensions" * K6 3D "AMD-K6(tm) 3D processor" * K6 3D+ ? */ static X86type x86amd[] = { { 5, 0, 23, "AMD-K5", }, /* guesswork */ { 5, 1, 23, "AMD-K5", }, /* guesswork */ { 5, 2, 23, "AMD-K5", }, /* guesswork */ { 5, 3, 23, "AMD-K5", }, /* guesswork */ { 5, 4, 23, "AMD Geode GX1", }, /* guesswork */ { 5, 5, 23, "AMD Geode GX2", }, /* guesswork */ { 5, 6, 11, "AMD-K6", }, /* trial and error */ { 5, 7, 11, "AMD-K6", }, /* trial and error */ { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ { 5, 9, 11, "AMD-K6-III", },/* trial and error */ { 5, 0xa, 23, "AMD Geode LX", }, /* guesswork */ { 6, 1, 11, "AMD-Athlon", },/* trial and error */ { 6, 2, 11, "AMD-Athlon", },/* trial and error */ { 4, -1, 22, "Am486", }, /* guesswork */ { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ { 6, -1, 11, "AMD-Athlon", },/* guesswork */ { 0xF, -1, 11, "AMD64", }, /* guesswork */ { -1, -1, 11, "unknown", }, /* total default */ }; /* * WinChip 240MHz */ static X86type x86winchip[] = { {5, 4, 23, "Winchip",}, /* guesswork */ {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, {6, 8, 23, "Via C3 Ezra-T",}, {6, 9, 23, "Via C3 Eden-N",}, { -1, -1, 23, "unknown", }, /* total default */ }; /* * SiS 55x */ static X86type x86sis[] = { {5, 0, 23, "SiS 55x",}, /* guesswork */ { -1, -1, 23, "unknown", }, /* total default */ }; static X86type *cputype; static void simplecycles(uvlong*); void (*cycles)(uvlong*) = simplecycles; void _cycles(uvlong*); /* in l.s */ static void simplecycles(uvlong*x) { *x = m->ticks; } void cpuidprint(void) { int i; char buf[128]; i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz); if(m->cpuidid[0]) i += sprint(buf+i, "%12.12s ", m->cpuidid); seprint(buf+i, buf + sizeof buf - 1, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n", m->cpuidtype, m->cpuidax, m->cpuiddx); print(buf); } /* * figure out: * - cpu type * - whether or not we have a TSC (cycle counter) * - whether or not it supports page size extensions * (if so turn it on) * - whether or not it supports machine check exceptions * (if so turn it on) * - whether or not it supports the page global flag * (if so turn it on) */ int cpuidentify(void) { char *p; int family, model, nomce; X86type *t, *tab; ulong cr4; ulong regs[4]; vlong mca, mct; cpuid(Highstdfunc, regs); memmove(m->cpuidid, ®s[1], BY2WD); /* bx */ memmove(m->cpuidid+4, ®s[3], BY2WD); /* dx */ memmove(m->cpuidid+8, ®s[2], BY2WD); /* cx */ m->cpuidid[12] = '\0'; cpuid(Procsig, regs); m->cpuidax = regs[0]; m->cpuiddx = regs[3]; if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0 || strncmp(m->cpuidid, "Geode by NSC", 12) == 0) tab = x86amd; else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0) tab = x86winchip; else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0) tab = x86sis; else tab = x86intel; family = X86FAMILY(m->cpuidax); model = X86MODEL(m->cpuidax); for(t=tab; t->name; t++) if((t->family == family && t->model == model) || (t->family == family && t->model == -1) || (t->family == -1)) break; m->cpuidtype = t->name; /* * if there is one, set tsc to a known value */ if(m->cpuiddx & Tsc){ m->havetsc = 1; cycles = _cycles; if(m->cpuiddx & Cpumsr) wrmsr(0x10, 0); } /* * use i8253 to guess our cpu speed */ guesscpuhz(t->aalcycles); /* * If machine check exception, page size extensions or page global bit * are supported enable them in CR4 and clear any other set extensions. * If machine check was enabled clear out any lingering status. */ if(m->cpuiddx & (Pge|Mce|0x8)){ cr4 = 0; if(m->cpuiddx & 0x08) cr4 |= 0x10; /* page size extensions */ if(p = getconf("*nomce")) nomce = strtoul(p, 0, 0); else nomce = 0; if((m->cpuiddx & Mce) && !nomce){ cr4 |= 0x40; /* machine check enable */ if(family == 5){ rdmsr(0x00, &mca); rdmsr(0x01, &mct); } } /* * Detect whether the chip supports the global bit * in page directory and page table entries. When set * in a particular entry, it means ``don't bother removing * this from the TLB when CR3 changes.'' * * We flag all kernel pages with this bit. Doing so lessens the * overhead of switching processes on bare hardware, * even more so on VMware. See mmu.c:/^memglobal. * * For future reference, should we ever need to do a * full TLB flush, it can be accomplished by clearing * the PGE bit in CR4, writing to CR3, and then * restoring the PGE bit. */ if(m->cpuiddx & Pge){ cr4 |= 0x80; /* page global enable bit */ m->havepge = 1; } putcr4(cr4); if(m->cpuiddx & Mce) rdmsr(0x01, &mct); } cputype = t; return t->family; } static long cputyperead(Chan*, void *a, long n, vlong offset) { char str[32]; ulong mhz; mhz = (m->cpuhz+999999)/1000000; snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); return readstr(offset, a, n, str); } static long archctlread(Chan*, void *a, long nn, vlong offset) { int n; char *buf, *p, *ep; p = buf = malloc(READSTR); ep = p + READSTR; p = seprint(p, ep, "cpu %s %lud%s\n", cputype->name, (ulong)(m->cpuhz+999999)/1000000, m->havepge ? " pge" : ""); p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off"); p = seprint(p, ep, "coherence "); if(coherence == mb386) p = seprint(p, ep, "mb386\n"); else if(coherence == mb586) p = seprint(p, ep, "mb586\n"); else if(coherence == mfence) p = seprint(p, ep, "mfence\n"); else if(coherence == nop) p = seprint(p, ep, "nop\n"); else p = seprint(p, ep, "0x%p\n", coherence); p = seprint(p, ep, "cmpswap "); if(cmpswap == cmpswap386) p = seprint(p, ep, "cmpswap386\n"); else if(cmpswap == cmpswap486) p = seprint(p, ep, "cmpswap486\n"); else p = seprint(p, ep, "0x%p\n", cmpswap); p = seprint(p, ep, "i8253set %s\n", doi8253set ? "on" : "off"); n = p - buf; n += mtrrprint(p, ep - p); buf[n] = '\0'; n = readstr(offset, a, nn, buf); free(buf); return n; } enum { CMpge, CMcoherence, CMi8253set, CMcache, }; static Cmdtab archctlmsg[] = { CMpge, "pge", 2, CMcoherence, "coherence", 2, CMi8253set, "i8253set", 2, CMcache, "cache", 4, }; static long archctlwrite(Chan*, void *a, long n, vlong) { uvlong base, size; Cmdbuf *cb; Cmdtab *ct; char *ep; cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg)); switch(ct->index){ case CMpge: if(!m->havepge) error("processor does not support pge"); if(strcmp(cb->f[1], "on") == 0) putcr4(getcr4() | 0x80); else if(strcmp(cb->f[1], "off") == 0) putcr4(getcr4() & ~0x80); else cmderror(cb, "invalid pge ctl"); break; case CMcoherence: if(strcmp(cb->f[1], "mb386") == 0) coherence = mb386; else if(strcmp(cb->f[1], "mb586") == 0){ if(X86FAMILY(m->cpuidax) < 5) error("invalid coherence ctl on this cpu family"); coherence = mb586; }else if(strcmp(cb->f[1], "mfence") == 0){ if((m->cpuiddx & Sse2) == 0) error("invalid coherence ctl on this cpu family"); coherence = mfence; }else if(strcmp(cb->f[1], "nop") == 0){ /* only safe on vmware */ if(conf.nmach > 1) error("cannot disable coherence on a multiprocessor"); coherence = nop; }else cmderror(cb, "invalid coherence ctl"); break; case CMi8253set: if(strcmp(cb->f[1], "on") == 0) doi8253set = 1; else if(strcmp(cb->f[1], "off") == 0){ doi8253set = 0; (*arch->timerset)(0); }else cmderror(cb, "invalid i2853set ctl"); break; case CMcache: base = strtoull(cb->f[1], &ep, 0); if(*ep) error("cache: parse error: base not a number?"); size = strtoull(cb->f[2], &ep, 0); if(*ep) error("cache: parse error: size not a number?"); mtrr(base, size, cb->f[3]); break; } free(cb); poperror(); return n; } void coherencyinit(void) { /* * Decide whether to use copy-on-reference (386 and mp). * We get another chance to set it in mpinit() for a * multiprocessor. */ if(X86FAMILY(m->cpuidax) == 3) conf.copymode = 1; if(X86FAMILY(m->cpuidax) >= 4) cmpswap = cmpswap486; if(X86FAMILY(m->cpuidax) >= 5) coherence = mb586; if(m->cpuiddx & Sse2) coherence = mfence; } void archinit(void) { PCArch **p; arch = 0; for(p = knownarch; *p; p++){ if((*p)->ident && (*p)->ident() == 0){ arch = *p; break; } } if(arch == 0) arch = &archgeneric; else{ if(arch->id == 0) arch->id = archgeneric.id; if(arch->reset == 0) arch->reset = archgeneric.reset; if(arch->serialpower == 0) arch->serialpower = archgeneric.serialpower; if(arch->modempower == 0) arch->modempower = archgeneric.modempower; if(arch->intrinit == 0) arch->intrinit = archgeneric.intrinit; if(arch->intrenable == 0) arch->intrenable = archgeneric.intrenable; } coherencyinit(); addarchfile("cputype", 0444, cputyperead, nil); addarchfile("archctl", 0664, archctlread, archctlwrite); } /* * call either the pcmcia or pccard device setup */ int pcmspecial(char *idstr, ISAConf *isa) { return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1; } /* * call either the pcmcia or pccard device teardown */ void pcmspecialclose(int a) { if (_pcmspecialclose != nil) _pcmspecialclose(a); } /* * return value and speed of timer set in arch->clockenable */ uvlong fastticks(uvlong *hz) { return (*arch->fastclock)(hz); } ulong µs(void) { return fastticks2us((*arch->fastclock)(nil)); } /* * set next timer interrupt */ void timerset(Tval x) { if(doi8253set) (*arch->timerset)(x); } , }, { 6, 0xB, 16, "PentiumIII/Xeon", }, { 6, 0xF, 16, "Xeon5000-series", }, { 6, 0x16, 16, "Core 2 (Intel 64)", }, { 6, 0x17, 16, "Core 2 (Intel 64)", }, { 6, 0x1c, 16, "Atom", }, { 0xF, 1, 169am/9pc/devp9am.c 664 0 0 41221 11334032360 11571ustar00nemosys/* * Support for 9am(8). */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "mp.h" #include "acpi.h" typedef struct Regio Regio; enum { Qdir = 0, Qctl, Qtbl, Qio, CMinit = 0, /* call hw init; everything has been configured */ CMregion, /* regio name spc base len accsz*/ CMgpe, /* gpe name id */ CMpwr, /* pwr state val */ CMdev, /* dev path name pnp uid addr */ CMpush, /* [ */ CMpop, /* ] */ CMirq, /* irq flags nb... */ CMoirq, /* oirq flags nb... */ CMdma, /* dma chans flags */ CModma, CMio, /* io min max align */ CMoio, CMmem, /* mem rw|ro min max align */ CMomem, CMas, /* as io|mem|bus flags mask min max len off attr */ CMoas, CMpir, /* pir dev pin link irq */ CMbbn, /* bbn n */ CMdebug, /* debug n */ CMdump, /* dump */ /* ACPI tbdf as encoded in acpi region base addresses */ Rpciregshift = 0, Rpciregmask = 0xFFFF, Rpcifunshift = 16, Rpcifunmask = 0xFFFF, Rpcidevshift = 32, Rpcidevmask = 0xFFFF, Rpcibusshift = 48, Rpcibusmask = 0xFFFF, }; struct Regio { void *arg; u8int (*get8)(uintptr, void*); void (*set8)(uintptr, u8int, void*); u16int (*get16)(uintptr, void*); void (*set16)(uintptr, u16int, void*); u32int (*get32)(uintptr, void*); void (*set32)(uintptr, u32int, void*); u64int (*get64)(uintptr, void*); void (*set64)(uintptr, u64int, void*); }; static Cmdtab ctls[] = { {CMinit, "init", 1}, {CMregion, "region", 6}, {CMgpe, "gpe", 3}, {CMpwr, "pwr", 4}, {CMdev, "dev", 6}, {CMpush, "[", 1}, {CMpop, "]", 1}, {CMirq, "irq", 0}, {CMoirq, "oirq", 0}, {CMdma, "dma", 3}, {CModma, "odma", 3}, {CMio, "io", 4}, {CMoio, "oio", 4}, {CMmem, "mem", 5}, {CMomem, "omem", 5}, {CMas, "as", 9}, {CMoas, "oas", 9}, {CMpir, "pir", 5}, {CMbbn, "bbn", 2}, {CMdebug, "debug", 2}, {CMdump, "dump", 1}, }; static Dirtab acpidir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "acpictl", {Qctl}, 0, 0666, "acpitbl", {Qtbl}, 0, DMEXCL|0444, "acpiregio", {Qio}, 0, 0666, }; static char* regnames[] = { "mem", "io", "pcicfg", "embed", "smb", "cmos", "pcibar", }; #define dprint if(debug)print #define Dprint if(debug>1)print #define dioprint if(debugio)print static Reg *reg; /* region used for I/O */ static int debug = 2; static int debugio = 0; static ACPIdev *devstack[16]; /* device config stack */ static int devdepth; static ACPIdev **lastptr; static ACPIdev *lastdev; ACPIdev *acpidevs; static u8int mget8(uintptr p, void*) { u8int *cp = (u8int*)p; return *cp; } static void mset8(uintptr p, u8int v, void*) { u8int *cp = (u8int*)p; *cp = v; } static u16int mget16(uintptr p, void*) { u16int *cp = (u16int*)p; return *cp; } static void mset16(uintptr p, u16int v, void*) { u16int *cp = (u16int*)p; *cp = v; } static u32int mget32(uintptr p, void*) { u32int *cp = (u32int*)p; return *cp; } static void mset32(uintptr p, u32int v, void*) { u32int *cp = (u32int*)p; *cp = v; } static u64int mget64(uintptr p, void*) { u64int *cp = (u64int*)p; return *cp; } static void mset64(uintptr p, u64int v, void*) { u64int *cp = (u64int*)p; *cp = v; } static u8int ioget8(uintptr p, void*) { return inb(p); } static void ioset8(uintptr p, u8int v, void*) { outb(p, v); } static u16int ioget16(uintptr p, void*) { return ins(p); } static void ioset16(uintptr p, u16int v, void*) { outs(p, v); } static u32int ioget32(uintptr p, void*) { return inl(p); } static void ioset32(uintptr p, u32int v, void*) { outl(p, v); } static u8int cfgget8(uintptr p, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; return pcicfgr8(&d, p); } static void cfgset8(uintptr p, u8int v, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; pcicfgw8(&d, p, v); } static u16int cfgget16(uintptr p, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; return pcicfgr16(&d, p); } static void cfgset16(uintptr p, u16int v, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; pcicfgw16(&d, p, v); } static u32int cfgget32(uintptr p, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; return pcicfgr32(&d, p); } static void cfgset32(uintptr p, u32int v, void* r) { Reg *ro = r; Pcidev d; d.tbdf = ro->tbdf; pcicfgw32(&d, p, v); } static Regio memio = { nil, mget8, mset8, mget16, mset16, mget32, mset32, mget64, mset64 }; static Regio ioio = { nil, ioget8, ioset8, ioget16, ioset16, ioget32, ioset32, nil, nil }; static Regio cfgio = { nil, cfgget8, cfgset8, cfgget16, cfgset16, cfgget32, cfgset32, nil, nil }; /* * Copy memory, 1/2/4/8-bytes at a time, to/from a region. */ static long regcpy(Regio *dio, uintptr da, Regio *sio, uintptr sa, long len, int align) { int n, i; if(0)dioprint("regcpy %#ulx %#ulx %#ulx %#ux\n", da, sa, len, align); if((len%align) != 0) print("regcpy: bug: copy not aligned. truncated\n"); n = len/align; for(i = 0; i < n; i++){ switch(align){ case 1: if(0)dioprint("cpy8 %#p %#p\n", da, sa); dio->set8(da, sio->get8(sa, sio->arg), dio->arg); break; case 2: if(0)dioprint("cpy16 %#p %#p\n", da, sa); dio->set16(da, sio->get16(sa, sio->arg), dio->arg); break; case 4: if(0)dioprint("cpy32 %#p %#p\n", da, sa); dio->set32(da, sio->get32(sa, sio->arg), dio->arg); break; case 8: if(0)dioprint("cpy64 %#p %#p\n", da, sa); dio->set64(da, sio->get64(sa, sio->arg), dio->arg); break; default: panic("regcpy: align bug"); } da += align; sa += align; } return n*align; } static void hdump(uchar *p, long len) { int i; if(len <= 4) print(" ->"); else print("\n\t"); for(i = 0; i < len; i++){ print("%02x ", p[i]); if((i % 16) == 0 && i > 0) print("\n"); } print("\n"); } /* * Perform I/O within region. All units in bytes. */ long acpiregio(Reg *r, void *p, ulong len, uintptr off, int iswr) { Regio rio; uintptr rp; if(r->accsz == 0){ print("acpi bug: acpiregio: accsz is 0\n"); r->accsz = 1; } if(debugio){ print("acpi:%s %s %#p %#ulx %#lx sz=%d", iswr ? "out" : "in ", r->name, p, off, len, r->accsz); if(iswr) hdump(p, len); } rp = 0; if(off + len > r->len){ print("\nregio: off %#p + len %#ulx > rlen %#ullx\n", off, len, r->len); len = r->len - off; } if(len <= 0){ dioprint("\n"); return 0; } switch(r->spc){ case Rsysmem: if(r->p != nil && r->plen < off+len){ vunmap(r->p, r->plen); r->p = nil; } if(r->p == nil){ r->plen = off+len; r->p = vmap(r->base, r->plen); } rp = (uintptr)r->p + off; rio = memio; break; case Rsysio: rp = r->base + off; rio = ioio; break; case Rpcicfg: rp = r->base + off; rio = cfgio; rio.arg = r; break; case Rpcibar: case Rembed: case Rsmbus: case Rcmos: case Ripmi: case Rfixedhw: print("\nregio: reg %s not supported\n", acpiregstr(r->spc)); error("region not supported"); } if(iswr) regcpy(&rio, rp, &memio, (uintptr)p, len, r->accsz); else{ regcpy(&memio, (uintptr)p, &rio, rp, len, r->accsz); if(debugio) hdump(p, len); } return len; } static char* tabs(int n) { static char s[10]; memset(s, '\t', sizeof(s)); if(n > 9) n = 9; s[n] = 0; return s; } static void dumpdevs(ACPIdev *, int); static void dumpdev(ACPIdev *d, int lvl) { ACPIres *r; print("%s%s %#p %s %s", tabs(lvl), d->name, d, d->acpiname, d->pnpid); print(" %lld %#ullx\n", d->uid, d->addr); for(r = d->res; r != nil; r = r->next) print("%s%R\n", tabs(lvl+1), r); for(r = d->other; r != nil; r = r->next) print("%sother %R\n", tabs(lvl+1), r); if(d->child != nil) dumpdevs(d->child, lvl+1); } static void dumpdevs(ACPIdev *nd, int lvl) { if(nd == nil) print("no acpi devs\n"); for(; nd != nil; nd = nd->cnext) dumpdev(nd, lvl); } static int hwgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) { Qid qid; if(i == DEVDOTDOT){ mkqid(&qid, Qdir, 0, QTDIR); devdir(c, qid, ".", 0, eve, 0555, dp); return 1; } i++; /* skip first element for . itself */ if(tab==0 || i>=ntab) return -1; tab += i; qid = tab->qid; qid.path &= ~Qdir; qid.vers = 0; devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); return 1; } static Chan* hwattach(char *spec) { return devattach(L'9', spec); } static void hwinit(void) { acpistart(); } static Walkqid* hwwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, acpidir, nelem(acpidir), hwgen); } static int hwstat(Chan *c, uchar *dp, int n) { return devstat(c, dp, n, acpidir, nelem(acpidir), hwgen); } /* * Tables can be huge. * We make acpitbl DMEXCL and release the read buffer * upon clunks of acpitbl. * Tables should be read only during boot, later they are useless * unless we are debugging 9am. */ static char *ttext; static Chan* hwopen(Chan *c, int omode) { long q; q = c->qid.path; if(q == Qtbl){ if(ttext != nil) error(Einuse); ttext = smprinttables(); } return devopen(c, omode, acpidir, nelem(acpidir), hwgen); } static void hwclose(Chan *c) { long q; q = c->qid.path; if(q == Qtbl){ free(ttext); ttext = nil; } } static long hwread(Chan *c, void *a, long n, vlong off) { long q; q = c->qid.path; switch(q){ case Qdir: return devdirread(c, a, n, acpidir, nelem(acpidir), hwgen); case Qtbl: if(ttext == nil) return 0; return readstr(off, a, n, ttext); case Qio: if(reg == nil) error("region not configured"); return acpiregio(reg, a, n, off, 0); } error(Eperm); return -1; } extern void startdevs(void); /* * Complete what once was done by main() to initialize * everything else. */ static void initctl(Cmdbuf *) { ulong s; static int alreadyinit; if(alreadyinit++ != 0) error("once is enough"); if(debug) dumpdevs(acpidevs, 0); s = spllo(); startdevs(); splx(s); } char* acpiregstr(int id) { static char buf[20]; /* BUG */ if(id >= 0 && id < nelem(regnames)) return regnames[id]; seprint(buf, buf+sizeof(buf), "spc:%#x", id); return buf; } static int acpiregid(char *s) { int i; for(i = 0; i < nelem(regnames); i++) if(strcmp(regnames[i], s) == 0) return i; return -1; } static void regionctl(Cmdbuf *cb) { Reg *r; uint rno, fun, dev, bus; r = reg; if(r == nil){ r = smalloc(sizeof(Reg)); r->name = nil; } if(r->p != nil) vunmap(r->p, r->plen); r->p = nil; r->plen = 0; kstrdup(&r->name, cb->f[1]); r->spc = acpiregid(cb->f[2]); if(r->spc < 0){ free(r); reg = nil; error("bad region type"); } if(r->spc == Rpcicfg || r->spc == Rpcibar){ rno = r->base>>Rpciregshift & Rpciregmask; fun = r->base>>Rpcifunshift & Rpcifunmask; dev = r->base>>Rpcidevshift & Rpcidevmask; bus = r->base>>Rpcibusshift & Rpcibusmask; r->tbdf = MKBUS(BusPCI, bus, dev, fun); r->base = rno; /* register ~ our base addr */ } r->base = strtoull(cb->f[3], nil, 0); r->len = strtoull(cb->f[4], nil, 0); r->accsz = strtoul(cb->f[5], nil, 0); if(r->accsz < 1 || r->accsz > 4){ free(r); reg = nil; error("bad region access size"); } reg = r; dioprint("acpi: region %s %s %llux %llux sz %d\n", r->name, acpiregstr(r->spc), r->base, r->len, r->accsz); } static void gpectl(Cmdbuf *cb) { int i; i = strtoul(cb->f[1], nil, 0); acpigpe(i, cb->f[2]); } static void pwrctl(Cmdbuf *cb) { int i, pm1a, pm1b; i = strtoul(cb->f[1], nil, 0); pm1a = strtoul(cb->f[2], nil, 0); pm1b = strtoul(cb->f[3], nil, 0); acpipwr(i, pm1a, pm1b); } /* * devices are never deallocated; not yet. */ static ACPIdev * newdev(void) { ACPIdev *d; static ACPIdev *last; d = smalloc(sizeof(ACPIdev)); if(last != nil) last->next = d; last = d; return d; } static void devctl(Cmdbuf *cb) { ACPIdev *d; if(lastptr == nil) lastptr = &acpidevs; lastdev = d = newdev(); kstrdup(&d->acpiname, cb->f[1]); kstrdup(&d->name, cb->f[2]); kstrdup(&d->pnpid, cb->f[3]); d->uid = strtoul(cb->f[4], nil, 0); d->addr = strtoull(cb->f[5], nil, 0); *lastptr = d; lastptr = &d->cnext; Dprint("acpidev %#p %s %s %s %ulld %#ullx\n", d, d->acpiname, d->name, d->pnpid, d->uid, d->addr); } static void pushctl(Cmdbuf *) { if(lastdev == nil) error("no device"); if(lastdev->child != nil) error("device has chilren"); if(devdepth == nelem(devstack)){ print("p9am bug: device depth\n"); error("bug: device depth exceeded"); } devstack[devdepth++] = lastdev; lastptr = &lastdev->child; lastdev = nil; Dprint("push\n"); } static void popctl(Cmdbuf *) { ACPIdev *d; if(devdepth == 0) error("no device in stack"); d = devstack[--devdepth]; lastptr = &d->cnext; assert(*lastptr == nil); lastdev = nil; Dprint("pop\n"); } static ACPIres* newres(int type, int excl, int other) { ACPIres *r, **l; if(lastdev == nil) error("no device"); l = &lastdev->res; if(other) l = &lastdev->other; for(; *l != nil; l = &(*l)->next) if(excl != 0 && (*l)->type == type) error("resource already declared"); /* will happen? */ *l = r = smalloc(sizeof(ACPIres)); r->type = type; return r; } static int strtointr(char *s) { int r; if(strlen(s) < 2) error("short interrupt flags"); if(strcmp(s, "none") == 0) return -1; switch(s[0]){ case 'l': r = PcmpLEVEL; break; case 'e': r = PcmpEDGE; break; default: r = 0; } switch(s[1]){ case 'l': r |= PcmpLOW; break; case 'h': r |= PcmpHIGH; break; } return r; } static void irqctl(Cmdbuf *cb) { ACPIres *r; int i; if(cb->nf < 3) error("usage: irq flags nb..."); r = newres(Rirq, 1, *cb->f[0] == 'o'); r->irq.flags = strtointr(cb->f[1]); for(i = 2; i < cb->nf && i-2 < nelem(r->irq.nb); i++) r->irq.nb[i-2] = strtoul(cb->f[i], nil, 0); if(i-2 < nelem(r->irq.nb)) r->irq.nb[i-2] = -1; if(i < cb->nf) print("p9am: bug: too many irqs for device\n"); Dprint("dev %s %R\n", lastdev->name, r); } static void dmactl(Cmdbuf *cb) { ACPIres *r; r = newres(Rdma, 1, *cb->f[0] == 'o'); r->dma.chans = strtoul(cb->f[1], nil, 0); r->irq.flags = strtoul(cb->f[2], nil, 0); Dprint("dev %s %R\n", lastdev->name, r); } static void ioctl(Cmdbuf *cb) { ACPIres *r; r = newres(Rio, 0, *cb->f[0] == 'o'); r->io.min = strtoull(cb->f[1], nil, 0); r->io.max = strtoull(cb->f[2], nil, 0); r->io.align = strtoul(cb->f[3], nil, 0); r->io.isrw = 1; Dprint("dev %s %R\n", lastdev->name, r); } static void memctl(Cmdbuf *cb) { ACPIres *r; r = newres(Rmem, 0, *cb->f[0] == 'o'); r->mem.isrw = strcmp(cb->f[1], "rw") == 0; r->mem.min = strtoull(cb->f[2], nil, 0); r->mem.max = strtoull(cb->f[3], nil, 0); r->mem.align = strtoul(cb->f[4], nil, 0); Dprint("dev %s %R\n", lastdev->name, r); } static void asctl(Cmdbuf *cb) { ACPIres *r; int type; type = -1; if(strcmp(cb->f[1], "io") == 0) type = Rioas; else if(strcmp(cb->f[1], "mem") == 0) type = Rmemas; else if(strcmp(cb->f[1], "bus") == 0) type = Rbusas; else error("unknown as type"); r = newres(type, 0, *cb->f[0] == 'o'); r->as.flags = strtoul(cb->f[2], nil, 0); r->as.mask = strtoull(cb->f[3], nil, 0); r->as.min = strtoull(cb->f[4], nil, 0); r->as.max = strtoull(cb->f[5], nil, 0); r->as.len = strtoull(cb->f[6], nil, 0); r->as.off = strtol(cb->f[7], nil, 0); r->as.attr = strtoull(cb->f[8], nil, 0); Dprint("dev %s %R\n", lastdev->name, r); } static void pirctl(Cmdbuf *cb) { ACPIres *r; r = newres(Rpir, 0, *cb->f[0] == 'o'); r->pir.dno = strtoul(cb->f[1], nil, 0); r->pir.pin = strtoul(cb->f[2], nil, 0); r->pir.link = strtoul(cb->f[3], nil, 0); r->pir.airq = strtoul(cb->f[4], nil, 0); Dprint("dev %s %R\n", lastdev->name, r); } static void bbnctl(Cmdbuf *cb) { ACPIres *r; r = newres(Rbbn, 0, *cb->f[0] == 'o'); r->bbn = strtoul(cb->f[1], nil, 0); Dprint("dev %s %R\n", lastdev->name, r); } static void debugctl(Cmdbuf *cb) { if(strcmp(cb->f[1], "on") == 0) debug = 1; else debug = strtol(cb->f[1], nil, 0); } static void dumpctl(Cmdbuf *) { int odbg; odbg = debug; debug = 2; dumpdevs(acpidevs, 0); debug = odbg; } typedef void (*Hwctl)(Cmdbuf*); static Hwctl hwctls[] = { [CMinit] initctl, [CMregion] regionctl, [CMgpe] gpectl, [CMpwr] pwrctl, [CMdev] devctl, [CMpush] pushctl, [CMpop] popctl, [CMirq] irqctl, [CMoirq] irqctl, [CMdma] dmactl, [CModma] dmactl, [CMio] ioctl, [CMoio] ioctl, [CMmem] memctl, [CMomem] memctl, [CMas] asctl, [CMoas] asctl, [CMpir] pirctl, [CMbbn] bbnctl, [CMdebug] debugctl, [CMdump] dumpctl, }; static long hwwrite(Chan *c, void *a, long n, vlong off) { Cmdtab *ct; Cmdbuf *cb; if(c->qid.path == Qio){ if(reg == nil) error("region not configured"); return acpiregio(reg, a, n, off, 1); } if(c->qid.path != Qctl) error(Eperm); cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, ctls, nelem(ctls)); if(ct->index < 0 || ct->index >= nelem(hwctls)) panic("p9am: hwwrite bug"); hwctls[ct->index](cb); poperror(); free(cb); return n; } Dev p9amdevtab = { L'9', "9am", devreset, hwinit, devshutdown, hwattach, hwwalk, hwstat, hwopen, devcreate, hwclose, hwread, devbread, hwwrite, devbwrite, devremove, devwstat, }; dhw: print("\nregio: reg %s not supported\n", acpiregstr(r->spc)); error("region not supported"); } if(iswr) regcpy(&rio, rp, &memio, (uintptr)p, len, r->accsz); else{ regcpy(&memio, (uintptr)p, &rio, rp, len, r->accsz); if(debugio) hdump(p, len); } return len; } static char* tabs(int n) { static char s[10]; memset(s, '\t', sizeof(s)); if(n >9am/9pc/main.c 664 0 0 40132 11334032360 11150ustar00nemosys#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "init.h" #include "pool.h" #include "reboot.h" Mach *m; /* * Where configuration info is left for the loaded programme. * This will turn into a structure as more is done by the boot loader * (e.g. why parse the .ini file twice?). * There are 3584 bytes available at CONFADDR. */ #define BOOTLINE ((char*)CONFADDR) #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) #define BOOTARGSLEN (4096-0x200-BOOTLINELEN) #define MAXCONF 64 char bootdisk[KNAMELEN]; Conf conf; char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; uchar *sp; /* user stack of init proc */ int delaylink; static void options(void) { long i, n; char *cp, *line[MAXCONF], *p, *q; /* * parse configuration args from dos file plan9.ini */ cp = BOOTARGS; /* where b.com leaves its config */ cp[BOOTARGSLEN-1] = 0; /* * Strip out '\r', change '\t' -> ' '. */ p = cp; for(q = cp; *q; q++){ if(*q == '\r') continue; if(*q == '\t') *q = ' '; *p++ = *q; } *p = 0; n = getfields(cp, line, MAXCONF, 1, "\n"); for(i = 0; i < n; i++){ if(*line[i] == '#') continue; cp = strchr(line[i], '='); if(cp == nil) continue; *cp++ = '\0'; confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } } extern void mmuinit0(void); extern void (*i8237alloc)(void); extern void acpiinit(void); void main(void) { mach0init(); options(); ioinit(); i8250console(); quotefmtinstall(); screeninit(); print("\nPlan 9\n"); trapinit0(); mmuinit0(); kbdinit(); i8253init(); cpuidentify(); meminit(); confinit(); archinit(); xinit(); if(i8237alloc != nil) i8237alloc(); trapinit(); printinit(); cpuidprint(); mmuinit(); acpiinit(); if(arch->intrinit) /* launches other processors on an mp */ arch->intrinit(); timersinit(); mathinit(); kbdenable(); if(arch->clockenable) arch->clockenable(); procinit0(); initseg(); chandevreset(0); pageinit(); swapinit(); bootlinks(); userinit(); active.thunderbirdsarego = 1; schedinit(); } void startdevs(void) { static int done; if(done++ > 0){ print("startdevs called more than once\n"); return; } if(0)print("startdevs\n"); links(); conf.monitor = 1; chandevreset(1); i8253link(); chandevinit(1); kproc("alarm", alarmkproc, 0); } void mach0init(void) { conf.nmach = 1; MACHP(0) = (Mach*)CPU0MACH; m->pdb = (ulong*)CPU0PDB; m->gdt = (Segdesc*)CPU0GDT; machinit(); active.machs = 1; active.exiting = 0; } void machinit(void) { int machno; ulong *pdb; Segdesc *gdt; machno = m->machno; pdb = m->pdb; gdt = m->gdt; memset(m, 0, sizeof(Mach)); m->machno = machno; m->pdb = pdb; m->gdt = gdt; m->perf.period = 1; /* * For polled uart output at boot, need * a default delay constant. 100000 should * be enough for a while. Cpuidentify will * calculate the real value later. */ m->loopconst = 100000; } void init0(void) { int i; char buf[2*KNAMELEN]; char *p; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(0); if(!waserror()){ snprint(buf, sizeof(buf), "%s %s", arch->id, conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "386", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++){ if(confname[i][0] != '*') ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } poperror(); } if((p = getconf("*9am")) == nil || strtol(p, 0, 0) == 0) startdevs(); spllo(); touser(sp); } void userinit(void) { void *v; Proc *p; Segment *s; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); p->fpstate = FPinit; fpoff(); /* * Kernel Stack * * N.B. make sure there's enough space for syscall to check * for valid args and * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack * * N.B. cannot call newpage() with clear=1, because pc kmap * requires up != nil. use tmpmap instead. */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(0, 0, USTKTOP-BY2PG); v = tmpmap(pg); memset(v, 0, BY2PG); segpage(s, pg); bootargs(v); tmpunmap(v); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(0, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); v = tmpmap(pg); memset(v, 0, BY2PG); memmove(v, initcode, sizeof initcode); tmpunmap(v); ready(p); } uchar * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } void bootargs(void *base) { int i, ac; uchar *av[32]; uchar **lsp; char *cp = BOOTLINE; char buf[64]; sp = (uchar*)base + BY2PG - MAXSYSARG*BY2WD; ac = 0; av[ac++] = pusharg("/386/9dos"); /* when boot is changed to only use rc, this code can go away */ cp[BOOTLINELEN-1] = 0; buf[0] = 0; if(strncmp(cp, "fd", 2) == 0){ sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; i < ac; i++) *lsp++ = av[i] + ((USTKTOP - BY2PG) - (ulong)base); *lsp = 0; sp += (USTKTOP - BY2PG) - (ulong)base - sizeof(ulong); } char* getconf(char *name) { int i; for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) return confval[i]; return 0; } static void writeconf(void) { char *p, *q; int n; p = getconfenv(); if(waserror()) { free(p); nexterror(); } /* convert to name=value\n format */ for(q=p; *q; q++) { q += strlen(q); *q = '='; q += strlen(q); *q = '\n'; } n = q - p + 1; if(n >= BOOTARGSLEN) error("kernel configuration too large"); memset(BOOTLINE, 0, BOOTLINELEN); memmove(BOOTARGS, p, n); poperror(); free(p); } void confinit(void) { char *p; int i, userpcnt; ulong kpages; if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; conf.npage = 0; for(i=0; i 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel fixed max + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } /* * can't go past the end of virtual memory * (ulong)-KZERO is 2^32 - KZERO */ if(kpages > ((ulong)-KZERO)/BY2PG) kpages = ((ulong)-KZERO)/BY2PG; conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(!cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ imagmem->maxsize = kpages; } } static char* mathmsg[] = { nil, /* handled below */ "denormalized operand", "division by zero", "numeric overflow", "numeric underflow", "precision loss", }; static void mathnote(void) { int i; ulong status; char *msg, note[ERRMAX]; status = up->fpsave.status; /* * Some attention should probably be paid here to the * exception masks and error summary. */ msg = "unknown exception"; for(i = 1; i <= 5; i++){ if(!((1<fpsave.pc, status); postnote(up, 1, note, NDebug); } /* * math coprocessor error */ static void matherror(Ureg *ur, void*) { /* * a write cycle to port 0xF0 clears the interrupt latch attached * to the error# line from the 387 */ if(!(m->cpuiddx & 0x01)) outb(0xF0, 0xFF); /* * save floating point state to check out error */ fpenv(&up->fpsave); mathnote(); if((ur->pc & 0xf0000000) == KZERO) panic("fp: status %ux fppc=0x%lux pc=0x%lux", up->fpsave.status, up->fpsave.pc, ur->pc); } /* * math coprocessor emulation fault */ static void mathemu(Ureg *ureg, void*) { if(up->fpstate & FPillegal){ /* someone did floating point in a note handler */ postnote(up, 1, "sys: floating point in note handler", NDebug); return; } switch(up->fpstate){ case FPinit: fpinit(); up->fpstate = FPactive; break; case FPinactive: /* * Before restoring the state, check for any pending * exceptions, there's no way to restore the state without * generating an unmasked exception. * More attention should probably be paid here to the * exception masks and error summary. */ if((up->fpsave.status & ~up->fpsave.control) & 0x07F){ mathnote(); break; } fprestore(&up->fpsave); up->fpstate = FPactive; break; case FPactive: panic("math emu pid %ld %s pc 0x%lux", up->pid, up->text, ureg->pc); break; } } /* * math coprocessor segment overrun */ static void mathover(Ureg*, void*) { pexit("math overrun", 0); } void mathinit(void) { trapenable(VectorCERR, matherror, 0, "matherror"); if(X86FAMILY(m->cpuidax) == 3) intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); trapenable(VectorCNA, mathemu, 0, "mathemu"); trapenable(VectorCSO, mathover, 0, "mathover"); } /* * set up floating point for a new process */ void procsetup(Proc*p) { p->fpstate = FPinit; fpoff(); } void procrestore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ /* * Fpsave() stores without handling pending * unmasked exeptions. Postnote() can't be called * here as sleep() already has up->rlock, so * the handling of pending exceptions is delayed * until the process runs again and generates an * emulation fault to activate the FPU. */ fpsave(&p->fpsave); } p->fpstate = FPinactive; } /* * While this processor is in the scheduler, the process could run * on another processor and exit, returning the page tables to * the free list where they could be reallocated and overwritten. * When this processor eventually has to get an entry from the * trashed page tables it will crash. * * If there's only one processor, this can't happen. * You might think it would be a win not to do this in that case, * especially on VMware, but it turns out not to matter. */ mmuflushtlb(PADDR(m->pdb)); } static void shutdown(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); /* * setting exiting will make hzclock() on each processor call exit(0), * which calls shutdown(0) and arch->reset(), which on mp systems is * mpshutdown, from which there is no return: the processor is idled * or initiates a reboot. clearing our bit in machs avoids calling * exit(0) from hzclock() on this processor. */ active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) iprint("cpu%d: exiting\n", m->machno); /* wait for any other processors to shutdown */ spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(active.ispanic){ if(!cpuserver) for(;;) halt(); if(getconf("*debug")) delay(5*60*1000); else delay(10000); }else delay(1000); } void reboot(void *entry, void *code, ulong size) { void (*f)(ulong, ulong, ulong); ulong *pdb; writeconf(); /* * the boot processor is cpu0. execute this function on it * so that the new kernel has the same cpu0. this only matters * because the hardware has a notion of which processor was the * boot processor and we look at it at start up. */ if (m->machno != 0) { procwired(up, 0); sched(); } shutdown(0); /* * should be the only processor running now */ if (m->machno != 0) print("on cpu%d (not 0)!\n", m->machno); if (active.machs) print("still have active ap processors!\n"); print("shutting down...\n"); delay(200); splhi(); /* turn off buffered serial console */ serialoq = nil; /* shutdown devices */ chandevshutdown(); arch->introff(); /* * Modify the machine page table to directly map the low 4MB of memory * This allows the reboot code to turn off the page mapping */ pdb = m->pdb; pdb[PDX(0)] = pdb[PDX(KZERO)]; mmuflushtlb(PADDR(pdb)); /* setup reboot trampoline function */ f = (void*)REBOOTADDR; memmove(f, rebootcode, sizeof(rebootcode)); print("rebooting...\n"); /* off we go - never to return */ coherence(); (*f)(PADDR(entry), PADDR(code), size); } void exit(int ispanic) { shutdown(ispanic); arch->reset(); } int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[32], *p; int i; snprint(cc, sizeof cc, "%s%d", class, ctlrno); p = getconf(cc); if(p == nil) return 0; isa->type = ""; isa->nopt = tokenize(p, isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } /* * put the processor in the halt state if we've no processes to run. * an interrupt will get us going again. */ void idlehands(void) { if(conf.nmach == 1) halt(); } p = getconfenv(); if(waserror()) { free(p); nexterror(); } /* convert to name=value\n format */ for(q=p; *q; q++) { q += strlen(q); *q = '='; q += strlen(q); *q = '\n'; } n = q - p + 1; if(n >= BOOTARGSLEN) error("kernel configuration too large"); memset(BOOTLINE, 0, BOOTLINELEN); memmove(BOOTARGS, p, n); poperror(); free(p); } void confinit(void) { char *p; int i, userpcnt; ulong kpages9am/9pc/mkfile 664 0 0 10176 11334032360 11257ustar00nemosysCONF=pc CONFLIST=pc pccpu pcf pccpuf pcdisk pcauth pcmpf CRAPLIST=pccd pcflop EXTRACOPIES= #EXTRACOPIES=lookout boundary # copy to these servers on install objtype=386 $p$stem.gz # pcflop and pccd need all the space they can get 9pcflop.gz: 9pcflop strip -o /fd/1 9pcflop | gzip -9 >9pcflop.gz 9pccd.gz: 9pccd strip -o /fd/1 9pccd | gzip -9 >9pccd.gz install:V: $p$CONF $p$CONF.gz cp $p$CONF $p$CONF.gz /$objtype/ for(i in $EXTRACOPIES) import $i / /n/$i && cp $p$CONF $p$CONF.gz /n/$i/$objtype/ <../boot/bootmkfile <../port/portmkfile <|../port/mkbootrules $CONF $ETHER: etherif.h ../port/netif.h ether8003.$O ether8390.$O: ether8390.h $VGA mouse.$O: screen.h devfloppy.$O: floppy.h mp.$O: mp.h acpi.h apbootstrap.h archmp.$O apic.$O: mp.h devp9am.$O acpi.$O archacpi.$O: mp.h acpi.h $SDEV: ../port/sd.h sd53c8xx.$O: sd53c8xx.i sdiahci.$O: ahci.h devaoe.$O sdaoe.$O: ../port/aoe.h main.$O: init.h reboot.h wavelan.$O: wavelan.c ../pc/wavelan.c ../pc/wavelan.h etherwavelan.$O: etherwavelan.c ../pc/wavelan.h devusb.$O usbuhci.$O usbohci.$O: usb.h trap.$O: /sys/include/tos.h uartaxp.$O: uartaxp.i etherm10g.$O: etherm10g2k.i etherm10g4k.i devp9am.$O: acpi.h init.h: ../port/initcode.c init9.c $CC ../port/initcode.c $CC init9.c $LD -l -R1 -o init.out init9.$O initcode.$O /386/lib/libc.a strip init.out {echo 'uchar initcode[]={' cat init.out | xd -1x | sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' echo '};'} > init.h reboot.h: rebootcode.s $AS rebootcode.s $LD -l -s -T0x11000 -R4 -o reboot.out rebootcode.$O {echo 'uchar rebootcode[]={' xd -1x reboot.out | sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' echo '};'} > reboot.h apbootstrap.h: apbootstrap.s mem.h $AS $prereq $LD -o apbootstrap.out -T$APBOOTSTRAP -R4 -l -s apbootstrap.$O {echo 'uchar apbootstrap[]={' xd -1x apbootstrap.out | sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' echo '};'} > $target sd53c8xx.i: sd53c8xx.n aux/na $prereq > $target uartaxp.i: a100p.cp {echo 'static uchar uartaxpcp[] = {' xd -1x $prereq | sed -e 's/^[0-9a-f]+ //' -e '/^$/d' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g' echo '};' } > $target acid:V: 8c -a -w -I. i8253.c>acid %.checkether:VQ: for (i in ether*.c){ x=`{echo $i | sed 's/\.c//'} if(! ~ $x ether8390 && ! grep -s '^ '^$x^'([ ]|$)' $stem) echo $x not included in $stem } exit 0 %.checkvga:VQ: for (i in vga*.c){ x=`{echo $i | sed 's/\.c//'} if(! ~ $x vga vgax vgasavage && ! grep -s '^ '^$x^'([ ]|$)' $stem) echo $x not included in $stem } exit 0 checkdist:VQ: for(i in pcdisk pcflop) for(j in checkvga checkether) mk $i.$j %.clean:V: rm -f $stem.c [9bz]$stem [9bz]$stem.gz boot$stem.* reboot.h apbootstrap.h init.h # testing 9load:D: /usr/rsc/boot/$O.load 9pcload cat $prereq >$target 9load.flp: 9load disk/format -b /386/pbs -df $target $prereq $p$CONF.flp: /386/9load plan9.ini $p$CONF.gz disk/format -b /386/pbs -df $target $prereq ore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ /* * Fpsave() stores without handling pending * unmasked exeptions. Postnote() can't 9am/9pc/mp.c 664 0 0 41157 11334032360 10650ustar00nemosys#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "mp.h" #include "acpi.h" #include "apbootstrap.h" static Lock mprdthilock; static int mprdthi; static Apic mpapic[MaxAPICNO+1]; Apic* mplapics; Apic* mpioapics; static int lastlapic = -1; static int lastioapic = -1; static int nmpapic; static Bus* mpbus; static Bus* mpbuslast; static Ref mpvnoref; /* unique vector assignment */ static int mpisabus = -1; static int mpeisabus = -1; static int debug = 1; #define dprint if(debug)print Apic* mpnewapic(int type, int no, int flags) { Apic *apic; dprint("apic type %d no %d %#ux\n", type, no, flags); apic = &mpapic[nmpapic]; apic->type = type; apic->apicno = no; apic->flags = flags; apic->lintr[0] = apic->lintr[1] = ApicIMASK; if(type == PcmpPROCESSOR){ if(lastlapic >= 0) mpapic[lastlapic].next = apic; else mplapics = apic; lastlapic = nmpapic; }else{ if(lastioapic >= 0) mpapic[lastioapic].next = apic; else mpioapics = apic; lastioapic = nmpapic; } nmpapic++; return apic; } static Apic* mpgetapic(int type, int no) { Apic *apic; if(type == PcmpPROCESSOR) apic = mplapics; else apic = mpioapics; for(; apic != nil; apic = apic->next) if(apic->apicno == no) return apic; return nil; } Bus* mpnewbus(int type, int busno) { Bus *bus; dprint("mpnewbus %d %d\n", type, busno); bus = xalloc(sizeof(Bus)); if(mpbus) mpbuslast->next = bus; else mpbus = bus; mpbuslast = bus; if(type == BusISA){ if(mpisabus != -1) print("mkbus: more than one ISA bus\n"); mpisabus = busno; } if(type == BusEISA){ if(mpeisabus != -1) print("mkbus: more than one EISA bus\n"); mpeisabus = busno; } bus->type = type; bus->busno = busno; if(bus->type == BusEISA){ bus->po = PcmpLOW; bus->el = PcmpLEVEL; } else if(bus->type == BusPCI){ bus->po = PcmpLOW; bus->el = PcmpLEVEL; } else if(bus->type == BusISA){ bus->po = PcmpHIGH; bus->el = PcmpEDGE; } else{ bus->po = PcmpHIGH; bus->el = PcmpEDGE; } return bus; } static Bus* mpgetbus(int type, int busno) { Bus *bus; for(bus = mpbus; bus; bus = bus->next){ if(type != -1 && type != bus->type) continue; if(busno == -1 || busno == bus->busno) return bus; } print("mpgetbus: can't find bus %d\n", busno); return nil; } static int mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/) { int el, po, v; /* * Parse an I/O or Local APIC interrupt table entry and * return the encoded vector. */ v = vno; po = intr->flags & PcmpPOMASK; el = intr->flags & PcmpELMASK; switch(intr->intr){ default: /* PcmpINT */ v |= ApicLOWEST; break; case PcmpNMI: v |= ApicNMI; po = PcmpHIGH; el = PcmpEDGE; break; case PcmpSMI: v |= ApicSMI; break; case PcmpExtINT: v |= ApicExtINT; /* * The AMI Goliath doesn't boot successfully with it's LINTR0 * entry which decodes to low+level. The PPro manual says ExtINT * should be level, whereas the Pentium is edge. Setting the * Goliath to edge+high seems to cure the problem. Other PPro * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes * to edge+high, so who knows. * Perhaps it would be best just to not set an ExtINT entry at * all, it shouldn't be needed for SMP mode. */ po = PcmpHIGH; el = PcmpEDGE; break; } /* */ if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<po; if(po == PcmpLOW) v |= ApicLOW; else if(po != PcmpHIGH){ print("mpintrinit: bad polarity 0x%uX\n", po); return ApicIMASK; } if(!el) el = bus->el; if(el == PcmpLEVEL) v |= ApicLEVEL; else if(el != PcmpEDGE){ print("mpintrinit: bad trigger 0x%uX\n", el); return ApicIMASK; } return v; } Aintr* mpnewintr(PCMPintr *p) { Bus *bus; Aintr *aintr; /* * According to the MultiProcessor Specification, a destination * I/O APIC of 0xFF means the signal is routed to all I/O APICs. * It's unclear how that can possibly be correct so treat it as * an error for now. */ if(p->apicno == 0xFF) return 0; if((bus = mpgetbus(-1, p->busno)) == 0){ dprint("mpnewintr: bus %d not found\n", p->busno); return 0; } aintr = xalloc(sizeof(Aintr)); aintr->intr = xalloc(sizeof(PCMPintr)); *aintr->intr = *p; dprint("iointr: type %d intr type %d flags %#ux " "bus %d irq %d apicno %d intin %d\n", p->type, p->intr, p->flags, p->busno, p->irq, p->apicno, p->intin); aintr->apic = mpgetapic(PcmpIOAPIC, p->apicno); if(aintr->apic == nil) print("mpnewintr: no ioapic %d\n", p->apicno); aintr->next = bus->aintr; bus->aintr = aintr; return aintr; } int mpnewlintr(PCMPintr* p) { Apic *apic; Bus *bus; int intin, v, i; /* * The offsets of vectors for LINT[01] are known to be * 0 and 1 from the local APIC vector space at VectorLAPIC. */ if((bus = mpgetbus(-1, p->busno)) == 0) return 0; intin = p->intin; /* * Pentium Pros have problems if LINT[01] are set to ExtINT * so just bag it, SMP mode shouldn't need ExtINT anyway. */ if(p->intr == PcmpExtINT || p->intr == PcmpNMI) v = ApicIMASK; else v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq); if(p->apicno == 0xFF){ for(i = 0; i <= MaxAPICNO; i++){ apic = mpgetapic(PcmpPROCESSOR, i); if(apic != nil && (apic->flags & PcmpEN)) apic->lintr[intin] = v; } } else{ apic = mpgetapic(PcmpPROCESSOR, p->apicno); if(apic != nil && (apic->flags & PcmpEN)) apic->lintr[intin] = v; } dprint("mp lintr apic %d in %d busno %d v %#ux\n", p->apicno, intin, p->busno, v); return v; } static void checkmtrr(void) { int i, vcnt; Mach *mach0; /* * If there are MTRR registers, snarf them for validation. */ if(!(m->cpuiddx & 0x1000)) return; rdmsr(0x0FE, &m->mtrrcap); rdmsr(0x2FF, &m->mtrrdef); if(m->mtrrcap & 0x0100){ rdmsr(0x250, &m->mtrrfix[0]); rdmsr(0x258, &m->mtrrfix[1]); rdmsr(0x259, &m->mtrrfix[2]); for(i = 0; i < 8; i++) rdmsr(0x268+i, &m->mtrrfix[(i+3)]); } vcnt = m->mtrrcap & 0x00FF; if(vcnt > nelem(m->mtrrvar)) vcnt = nelem(m->mtrrvar); for(i = 0; i < vcnt; i++) rdmsr(0x200+i, &m->mtrrvar[i]); /* * If not the bootstrap processor, compare. */ if(m->machno == 0) return; mach0 = MACHP(0); if(mach0->mtrrcap != m->mtrrcap) print("mtrrcap%d: %lluX %lluX\n", m->machno, mach0->mtrrcap, m->mtrrcap); if(mach0->mtrrdef != m->mtrrdef) print("mtrrdef%d: %lluX %lluX\n", m->machno, mach0->mtrrdef, m->mtrrdef); for(i = 0; i < 11; i++){ if(mach0->mtrrfix[i] != m->mtrrfix[i]) print("mtrrfix%d: i%d: %lluX %lluX\n", m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]); } for(i = 0; i < vcnt; i++){ if(mach0->mtrrvar[i] != m->mtrrvar[i]) print("mtrrvar%d: i%d: %lluX %lluX\n", m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]); } } static void mprdthiw(Apic *apic, int intin, int lo) { lock(&mprdthilock); ioapicrdtw(apic, intin, mprdthi, lo); unlock(&mprdthilock); } static void squidboy(Apic* apic) { if(debug) iprint("Hello Squidboy\n"); machinit(); mmuinit(); cpuidentify(); cpuidprint(); checkmtrr(); lock(&mprdthilock); mprdthi |= (1<apicno)<<24; unlock(&mprdthilock); lapicinit(apic); lapiconline(); syncclock(); timersinit(); fpoff(); lock(&active); active.machs |= 1<machno; unlock(&active); while(!active.thunderbirdsarego) microdelay(100); schedinit(); } static void mpstartap(Apic* apic) { ulong *apbootp, *pdb, *pte; Mach *mach, *mach0; int i, machno; uchar *p; mach0 = MACHP(0); /* * Initialise the AP page-tables and Mach structure. The page-tables * are the same as for the bootstrap processor with the exception of * the PTE for the Mach structure. * * Xspanalloc will panic if an allocation can't be made. */ p = xspanalloc(4*BY2PG, BY2PG, 0); pdb = (ulong*)p; memmove(pdb, mach0->pdb, BY2PG); p += BY2PG; if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil){ iprint("mpstartap: can't mmuwalk to mach\n"); return; } memmove(p, KADDR(PPN(*pte)), BY2PG); *pte = PADDR(p)|PTEWRITE|PTEVALID; if(mach0->havepge) *pte |= PTEGLOBAL; p += BY2PG; mach = (Mach*)p; if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil) return; *pte = PADDR(mach)|PTEWRITE|PTEVALID; if(mach0->havepge) *pte |= PTEGLOBAL; p += BY2PG; machno = apic->machno; MACHP(machno) = mach; mach->machno = machno; mach->pdb = pdb; mach->gdt = (Segdesc*)p; /* filled by mmuinit */ /* * Tell the AP where its kernel vector and pdb are. * The offsets are known in the AP bootstrap code. */ apbootp = (ulong*)(APBOOTSTRAP+0x08); *apbootp++ = (ulong)squidboy; *apbootp++ = PADDR(pdb); *apbootp = (ulong)apic; /* * Universal Startup Algorithm. */ p = KADDR(0x467); *p++ = PADDR(APBOOTSTRAP); *p++ = PADDR(APBOOTSTRAP)>>8; i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16; /* code assumes i==0 */ if(i != 0) iprint("mp: bad APBOOTSTRAP\n"); *p++ = i; *p = i>>8; nvramwrite(0x0F, 0x0A); lapicstartap(apic, PADDR(APBOOTSTRAP)); for(i = 0; i < 1000; i++){ lock(&mprdthilock); if(mprdthi & ((1<apicno)<<24)){ unlock(&mprdthilock); break; } unlock(&mprdthilock); delay(10); } nvramwrite(0x0F, 0x00); } void mpstart(Apic *bpapic) { int ncpu; char *cp; Apic *apic; dprint("bpapic lint0 %#ux lint1 %#ux...\n", bpapic->lintr[0], bpapic->lintr[1]); lapicinit(bpapic); lock(&mprdthilock); mprdthi |= (1<apicno)<<24; unlock(&mprdthilock); /* * These interrupts are local to the processor * and do not appear in the I/O APIC so it is OK * to set them now. */ dprint("local intrs...\n"); intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock"); intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror"); intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious"); dprint("lapic online...\n"); lapiconline(); checkmtrr(); /* * Initialise the application processors. */ if(cp = getconf("*ncpu")){ ncpu = strtol(cp, 0, 0); if(ncpu < 1) ncpu = 1; } else ncpu = MaxAPICNO; memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap)); conf.nmach = 1; for(apic = mplapics; apic != nil; apic = apic->next){ if(ncpu <= 1) break; if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){ mpstartap(apic); conf.nmach++; ncpu--; } } /* * we don't really know the number of processors till * here. * * set conf.copymode here if nmach > 1. * Should look for an ExtINT line and enable it. */ if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1) conf.copymode = 1; dprint("nmach = %ld\n", conf.nmach); } static int mpintrenablex(Vctl* v, int tbdf) { Bus *bus; Aintr *aintr; Apic *apic; Pcidev *pcidev; int bno, dno, irq, lo, n, type, vno; /* * Find the bus. */ type = BUSTYPE(tbdf); bno = BUSBNO(tbdf); dno = BUSDNO(tbdf); if(type == BusISA) bno = mpisabus; bus = mpgetbus(type, bno); if(bus == nil){ print("mpintrenablex: can't find bus type %d\n", type); return -1; } /* * For PCI devices the interrupt pin (INT[ABCD]) and device * number are encoded into the entry irq field, so create something * to match on. The interrupt pin used by the device has to be * obtained from the PCI config space. */ if(bus->type == BusPCI){ pcidev = pcimatchtbdf(tbdf); if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) irq = (dno<<2)|(n-1); else irq = -1; //print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq); } else irq = v->irq; /* * Find a matching interrupt entry from the list of interrupts * attached to this bus. */ for(aintr = bus->aintr; aintr; aintr = aintr->next){ if(aintr->intr->irq != irq) continue; if (0) { PCMPintr* p = aintr->intr; print("mpintrenablex: bus %d intin %d irq %d\n", p->busno, p->intin, p->irq); } /* * Check if already enabled. Multifunction devices may share * INT[A-D]# so, if already enabled, check the polarity matches * and the trigger is level. * * Should check the devices differ only in the function number, * but that can wait for the planned enable/disable rewrite. * The RDT read here is safe for now as currently interrupts * are never disabled once enabled. */ apic = aintr->apic; ioapicrdtr(apic, aintr->intr->intin, 0, &lo); if(!(lo & ApicIMASK)){ vno = lo & 0xFF; //print("%s vector %d (!imask)\n", v->name, vno); n = mpintrinit(bus, aintr->intr, vno, v->irq); n |= ApicLOGICAL; lo &= ~(ApicRemoteIRR|ApicDELIVS); if(n != lo || !(n & ApicLEVEL)){ print("mpintrenablex: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", v->irq, tbdf, lo, n); return -1; } v->isr = lapicisr; v->eoi = lapiceoi; return vno; } /* * With the APIC a unique vector can be assigned to each * request to enable an interrupt. There are two reasons this * is a good idea: * 1) to prevent lost interrupts, no more than 2 interrupts * should be assigned per block of 16 vectors (there is an * in-service entry and a holding entry for each priority * level and there is one priority level per block of 16 * interrupts). * 2) each input pin on the IOAPIC will receive a different * vector regardless of whether the devices on that pin use * the same IRQ as devices on another pin. */ vno = VectorAPIC + (incref(&mpvnoref)-1)*8; if(vno > MaxVectorAPIC){ print("mpintrenablex: vno %d, irq %d, tbdf %uX\n", vno, v->irq, tbdf); return -1; } lo = mpintrinit(bus, aintr->intr, vno, v->irq); //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", // lo, bus->busno, aintr->intr->irq, vno, // v->irq, i8259elcr); if(lo & ApicIMASK) return -1; lo |= ApicLOGICAL; if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC) mprdthiw(apic, aintr->intr->intin, lo); //else // print("lo not enabled 0x%uX %d\n", // apic->flags, apic->type); v->isr = lapicisr; v->eoi = lapiceoi; return vno; } return -1; } int mpintrenable(Vctl* v) { int irq, tbdf, vno; /* * If the bus is known, try it. * BUSUNKNOWN is given both by [E]ISA devices and by * interrupts local to the processor (local APIC, coprocessor * breakpoint and page-fault). */ tbdf = v->tbdf; if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) return vno; irq = v->irq; if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ if(irq != IrqSPURIOUS) v->isr = lapiceoi; return VectorPIC+irq; } if(irq < 0 || irq > MaxIrqPIC){ print("mpintrenable: irq %d out of range\n", irq); return -1; } /* * Either didn't find it or have to try the default buses * (ISA and EISA). This hack is due to either over-zealousness * or laziness on the part of some manufacturers. * * The MP configuration table on some older systems * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus * but none for ISA. It also has the interrupt type and * polarity set to 'default for this bus' which wouldn't * be compatible with ISA. */ if(mpeisabus != -1){ vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); if(vno != -1) return vno; } if(mpisabus != -1){ vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); if(vno != -1) return vno; } print("mpintrenable: out of choices eisa %d isa %d tbdf %#ux irq %d\n", mpeisabus, mpisabus, v->tbdf, v->irq); return -1; } static Lock mpshutdownlock; void mpshutdown(void) { /* * To be done... */ if(!canlock(&mpshutdownlock)){ /* * If this processor received the CTRL-ALT-DEL from * the keyboard, acknowledge it. Send an INIT to self. */ #ifdef FIXTHIS if(lapicisr(VectorKBD)) lapiceoi(VectorKBD); #endif /* FIX THIS */ arch->introff(); idle(); } print("apshutdown: active = 0x%2.2uX\n", active.machs); delay(1000); splhi(); /* * INIT all excluding self. */ lapicicrw(0, 0x000C0000|ApicINIT); acpireset(); pcireset(); i8042reset(); /* * Often the BIOS hangs during restart if a conventional 8042 * warm-boot sequence is tried. The following is Intel specific and * seems to perform a cold-boot, but at least it comes back. * And sometimes there is no keyboard... * * The reset register (0xcf9) is usually in one of the bridge * chips. The actual location and sequence could be extracted from * ACPI but why bother, this is the end of the line anyway. */ print("no kbd; trying bios warm boot..."); *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */ outb(0xCF9, 0x02); outb(0xCF9, 0x06); print("can't reset\n"); for(;;) idle(); } Lock mpsynclock; void syncclock(void) { uvlong x; if(arch->fastclock != tscticks) return; if(m->machno == 0){ wrmsr(0x10, 0); m->tscticks = 0; } else { x = MACHP(0)->tscticks; while(x == MACHP(0)->tscticks) ; wrmsr(0x10, MACHP(0)->tscticks); cycles(&m->tscticks); } } uvlong tscticks(uvlong *hz) { if(hz != nil) *hz = m->cpuhz; cycles(&m->tscticks); /* Uses the rdtsc instruction */ return m->tscticks; } print("mtrrvar%d: i%d: %lluX %lluX\n", m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]); } } static void mprdthiw(Apic *apic, int intin, int lo) { lock(&mprdthilock); ioapicrdtw(apic, intin, mprdthi, lo); unlock(&mprdthilock); } static void squidboy(Apic* apic) { if(debug) iprint("Hello Squidboy\n"); machinit(); mmuinit(); cpuidentify(); cpuidprint(); checkmtrr(); lock(&mpr9am/9pc/mp.h 664 0 0 16144 11334032360 10653ustar00nemosys/* * MultiProcessor Specification Version 1.[14]. */ typedef struct { /* floating pointer */ uchar signature[4]; /* "_MP_" */ long physaddr; /* physical address of MP configuration table */ uchar length; /* 1 */ uchar specrev; /* [14] */ uchar checksum; /* all bytes must add up to 0 */ uchar type; /* MP system configuration type */ uchar imcrp; uchar reserved[3]; } _MP_; typedef struct { /* configuration table header */ uchar signature[4]; /* "PCMP" */ ushort length; /* total table length */ uchar version; /* [14] */ uchar checksum; /* all bytes must add up to 0 */ uchar product[20]; /* product id */ ulong oemtable; /* OEM table pointer */ ushort oemlength; /* OEM table length */ ushort entry; /* entry count */ ulong lapicbase; /* address of local APIC */ ushort xlength; /* extended table length */ uchar xchecksum; /* extended table checksum */ uchar reserved; } PCMP; typedef struct { /* processor table entry */ uchar type; /* entry type (0) */ uchar apicno; /* local APIC id */ uchar version; /* local APIC verison */ uchar flags; /* CPU flags */ uchar signature[4]; /* CPU signature */ ulong feature; /* feature flags from CPUID instruction */ uchar reserved[8]; } PCMPprocessor; typedef struct { /* bus table entry */ uchar type; /* entry type (1) */ uchar busno; /* bus id */ char string[6]; /* bus type string */ } PCMPbus; typedef struct { /* I/O APIC table entry */ uchar type; /* entry type (2) */ uchar apicno; /* I/O APIC id */ uchar version; /* I/O APIC version */ uchar flags; /* I/O APIC flags */ ulong addr; /* I/O APIC address */ } PCMPioapic; typedef struct { /* interrupt table entry */ uchar type; /* entry type ([34]) */ uchar intr; /* interrupt type */ ushort flags; /* interrupt flag */ uchar busno; /* source bus id */ uchar irq; /* source bus irq */ uchar apicno; /* destination APIC id */ uchar intin; /* destination APIC [L]INTIN# */ } PCMPintr; typedef struct { /* system address space mapping entry */ uchar type; /* entry type (128) */ uchar length; /* of this entry (20) */ uchar busno; /* bus id */ uchar addrtype; ulong addrbase[2]; ulong addrlength[2]; } PCMPsasm; typedef struct { /* bus hierarchy descriptor entry */ uchar type; /* entry type (129) */ uchar length; /* of this entry (8) */ uchar busno; /* bus id */ uchar info; /* bus info */ uchar parent; /* parent bus */ uchar reserved[3]; } PCMPhierarchy; typedef struct { /* compatibility bus address space modifier entry */ uchar type; /* entry type (130) */ uchar length; /* of this entry (8) */ uchar busno; /* bus id */ uchar modifier; /* address modifier */ ulong range; /* predefined range list */ } PCMPcbasm; enum { /* table entry types */ PcmpPROCESSOR = 0x00, /* one entry per processor */ PcmpBUS = 0x01, /* one entry per bus */ PcmpIOAPIC = 0x02, /* one entry per I/O APIC */ PcmpIOINTR = 0x03, /* one entry per bus interrupt source */ PcmpLINTR = 0x04, /* one entry per system interrupt source */ PcmpSASM = 0x80, PcmpHIERARCHY = 0x81, PcmpCBASM = 0x82, /* PCMPprocessor and PCMPioapic flags */ PcmpEN = 0x01, /* enabled */ PcmpBP = 0x02, /* bootstrap processor */ /* PCMPiointr and PCMPlintr flags */ PcmpPOMASK = 0x03, /* polarity conforms to specifications of bus */ PcmpHIGH = 0x01, /* active high */ PcmpLOW = 0x03, /* active low */ PcmpELMASK = 0x0C, /* trigger mode of APIC input signals */ PcmpEDGE = 0x04, /* edge-triggered */ PcmpLEVEL = 0x0C, /* level-triggered */ /* PCMPiointr and PCMPlintr interrupt type */ PcmpINT = 0x00, /* vectored interrupt from APIC Rdt */ PcmpNMI = 0x01, /* non-maskable interrupt */ PcmpSMI = 0x02, /* system management interrupt */ PcmpExtINT = 0x03, /* vectored interrupt from external PIC */ /* PCMPsasm addrtype */ PcmpIOADDR = 0x00, /* I/O address */ PcmpMADDR = 0x01, /* memory address */ PcmpPADDR = 0x02, /* prefetch address */ /* PCMPhierarchy info */ PcmpSD = 0x01, /* subtractive decode bus */ /* PCMPcbasm modifier */ PcmpPR = 0x01, /* predefined range list */ }; /* * Condensed form of the MP Configuration Table. * This is created during a single pass through the MP Configuration * table or during parsing of ACPI tables. */ typedef struct Aintr Aintr; typedef struct Bus Bus; typedef struct Apic Apic; typedef struct Bus { uchar type; uchar busno; uchar po; uchar el; Aintr* aintr; /* interrupts tied to this bus */ Bus* next; } Bus; typedef struct Aintr { PCMPintr* intr; Apic* apic; Aintr* next; }; typedef struct Apic { Apic* next; /* next Apic of the same type */ int type; int apicno; ulong* addr; /* register base address */ ulong paddr; int flags; /* PcmpBP|PcmpEN */ Lock; /* I/O APIC: register access */ int mre; /* I/O APIC: maximum redirection entry */ int lintr[2]; /* Local APIC */ int ibase; /* I/O APIC: acpi irq. base */ int machno; } Apic; enum { MaxAPICNO = 31, }; enum { /* I/O APIC registers */ IoapicID = 0x00, /* ID */ IoapicVER = 0x01, /* version */ IoapicARB = 0x02, /* arbitration ID */ IoapicRDT = 0x10, /* redirection table */ }; /* * Common bits for * I/O APIC Redirection Table Entry; * Local APIC Local Interrupt Vector Table; * Local APIC Inter-Processor Interrupt; * Local APIC Timer Vector Table. */ enum { ApicFIXED = 0x00000000, /* [10:8] Delivery Mode */ ApicLOWEST = 0x00000100, /* Lowest priority */ ApicSMI = 0x00000200, /* System Management Interrupt */ ApicRR = 0x00000300, /* Remote Read */ ApicNMI = 0x00000400, ApicINIT = 0x00000500, /* INIT/RESET */ ApicSTARTUP = 0x00000600, /* Startup IPI */ ApicExtINT = 0x00000700, ApicPHYSICAL = 0x00000000, /* [11] Destination Mode (RW) */ ApicLOGICAL = 0x00000800, ApicDELIVS = 0x00001000, /* [12] Delivery Status (RO) */ ApicHIGH = 0x00000000, /* [13] Interrupt Input Pin Polarity (RW) */ ApicLOW = 0x00002000, ApicRemoteIRR = 0x00004000, /* [14] Remote IRR (RO) */ ApicEDGE = 0x00000000, /* [15] Trigger Mode (RW) */ ApicLEVEL = 0x00008000, ApicIMASK = 0x00010000, /* [16] Interrupt Mask */ }; extern Aintr* mpnewintr(PCMPintr *p); extern Apic* mpnewapic(int type, int no, int flags); extern Bus* mpnewbus(int type, int busno); extern int lapiceoi(int v); extern int lapicisr(int v); extern int mpintrenable(Vctl* v); extern int mpnewlintr(PCMPintr* p); extern uvlong tscticks(uvlong *hz); extern void ioapicinit(Apic* apic, int apicno); extern void ioapicrdtr(Apic* apic, int sel, int* hi, int* lo); extern void ioapicrdtw(Apic* apic, int sel, int hi, int lo); extern void lapicclock(Ureg *u, void*); extern void lapicerror(Ureg*, void*); extern void lapicicrw(ulong hi, ulong lo); extern void lapicinit(Apic* apic); extern void lapicintroff(void); extern void lapicintron(void); extern void lapicnmidisable(void); extern void lapicnmienable(void); extern void lapiconline(void); extern void lapicspurious(Ureg*, void*); extern void lapicstartap(Apic* apic, int v); extern void lapictimerset(uvlong next); extern void mpshutdown(void); extern void mpstart(Apic *bpapic); extern void syncclock(void); extern Apic *mplapics; extern Apic *mpioapics; d * polarity set to 'default for this bus' which wouldn't * be compatible with ISA. */ if(mpeisabus != -1){ vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); if(vno != -1) return vno; } if(mpisabus != -1){ vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); if(vno != -1) return vno; } print("mpintrenable: out of choices eisa %d isa %d tbdf %#ux irq %d\n", mpeisabus, mpisabus, v->tbdf, v-9am/9pc/pcf 664 0 0 3303 11334032360 10532ustar00nemosys# pcf - pc terminal with fossil root and maybe venti block store dev root env p9am acpi cons proc dup srv mnt pipe arch pnp pci rtc ssl tls cap kprof fs ether netif ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno draw screen vga vgax mouse mouse vga kbmap kbin sd floppy dma aoe lpt audio dma pccard i82365 cis uart usb link realmode devpccard devi82365 apm apmjump ether2000 ether8390 ether2114x pci ether589 etherelnk3 ether79c970 pci ether8003 ether8390 ether8139 pci ether8169 pci ethermii ether82543gc pci ether82557 pci ether82563 pci ether83815 pci etherdp83820 pci etherec2t ether8390 etherelnk3 pci etherga620 pci etherigbe pci ethermii ethervgbe pci ethermii ethervt6102 pci ethermii ethervt6105m pci ethermii ethersink ethersmc devi82365 cis etherwavelan wavelan devi82365 cis pci ethermedium pcmciamodem netdevmedium loopbackmedium usbuhci usbohci usbehci misc archmp mp apic mtrr sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi sdiahci pci sdscsi sdaoe uarti8250 uartpci pci vga3dfx +cur vgaark2000pv +cur vgabt485 =cur vgaclgd542x +cur vgaclgd546x +cur vgact65545 +cur vgacyber938x +cur vgaet4000 +cur vgahiqvideo +cur vgai81x +cur vgamach64xx +cur vgamga2164w +cur vgamga4xx +cur vganeomagic +cur vganvidia +cur vgargb524 =cur vgas3 +cur vgasavage vgat2r4 +cur vgatvp3020 =cur vgatvp3026 =cur vgavesa vgavmware +cur ip tcp udp rudp ipifc icmp icmp6 gre ipmux esp port int cpuserver = 0; boot boot #S/sdC0/ tcp local bootdir bootpcf.out boot /386/bin/ip/ipconfig /386/bin/auth/factotum /386/bin/fossil/fossil /386/bin/9am ustar00nemosys9am/9pc/pcmpf 664 0 0 3306 11334032360 11072ustar00nemosys# pcf - pc terminal with fossil root and maybe venti block store dev root env p9am acpi cons proc dup srv mnt pipe arch pnp pci rtc ssl tls cap kprof fs ether netif ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno draw screen vga vgax mouse mouse vga kbmap kbin sd floppy dma aoe lpt audio dma pccard i82365 cis uart usb link realmode devpccard devi82365 apm apmjump ether2000 ether8390 ether2114x pci ether589 etherelnk3 ether79c970 pci ether8003 ether8390 ether8139 pci ether8169 pci ethermii ether82543gc pci ether82557 pci ether82563 pci ether83815 pci etherdp83820 pci etherec2t ether8390 etherelnk3 pci etherga620 pci etherigbe pci ethermii ethervgbe pci ethermii ethervt6102 pci ethermii ethervt6105m pci ethermii ethersink ethersmc devi82365 cis etherwavelan wavelan devi82365 cis pci ethermedium pcmciamodem netdevmedium loopbackmedium usbuhci usbohci usbehci misc archacpi mp apic mtrr sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi sdiahci pci sdscsi sdaoe uarti8250 uartpci pci vga3dfx +cur vgaark2000pv +cur vgabt485 =cur vgaclgd542x +cur vgaclgd546x +cur vgact65545 +cur vgacyber938x +cur vgaet4000 +cur vgahiqvideo +cur vgai81x +cur vgamach64xx +cur vgamga2164w +cur vgamga4xx +cur vganeomagic +cur vganvidia +cur vgargb524 =cur vgas3 +cur vgasavage vgat2r4 +cur vgatvp3020 =cur vgatvp3026 =cur vgavesa vgavmware +cur ip tcp udp rudp ipifc icmp icmp6 gre ipmux esp port int cpuserver = 0; boot boot #S/sdC0/ tcp local bootdir bootpcmpf.out boot /386/bin/ip/ipconfig /386/bin/auth/factotum /386/bin/fossil/fossil /386/bin/9am typedef struct { /* bus hierarchy descriptor entry */ uchar type; /* entry type (129) */ uchar length; /* of this entry (8) */ uchar busno; /* bus id */ uchar info; /* bus info */ uchar parent; /* parent bus */ uchar reserved[3]; } PCMPhierarchy; typedef struct { /* compatibility bus address 9am/9port/ 775 0 0 0 11334032360 104225ustar00nemosys9am/9port/chan.c 664 0 0 104375 11334032360 11551ustar00nemosys#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" int chandebug=0; /* toggled by sysr1 */ #define DBG if(chandebug)iprint enum { PATHSLOP = 20, PATHMSLOP = 20, }; struct { Lock; int fid; Chan *free; Chan *list; }chanalloc; typedef struct Elemlist Elemlist; struct Elemlist { char *aname; /* original name */ char *name; /* copy of name, so '/' can be overwritten */ int nelems; char **elems; int *off; int mustbedir; int nerror; int prefix; }; #define SEP(c) ((c) == 0 || (c) == '/') static void dumpmount(void) /* DEBUGGING */ { Pgrp *pg; Mount *t; Mhead **h, **he, *f; if(up == nil){ print("no process for dumpmount\n"); return; } pg = up->pgrp; if(pg == nil){ print("no pgrp for dumpmount\n"); return; } rlock(&pg->ns); if(waserror()){ runlock(&pg->ns); nexterror(); } he = &pg->mnthash[MNTHASH]; for(h = pg->mnthash; h < he; h++){ for(f = *h; f; f = f->hash){ print("head: %#p: %s %#llux.%lud %C %lud -> \n", f, f->from->path->s, f->from->qid.path, f->from->qid.vers, devtab[f->from->type]->dc, f->from->dev); for(t = f->mount; t; t = t->next) print("\t%#p: %s (umh %#p) (path %#.8llux dev %C %lud)\n", t, t->to->path->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev); } } poperror(); runlock(&pg->ns); } char* chanpath(Chan *c) { if(c == nil) return ""; if(c->path == nil) return ""; if(c->path->s == nil) return ""; return c->path->s; } int isdotdot(char *p) { return p[0]=='.' && p[1]=='.' && p[2]=='\0'; } long incref(Ref *r) { long x; lock(r); x = ++r->ref; unlock(r); return x; } long decref(Ref *r) { long x; lock(r); x = --r->ref; unlock(r); if(x < 0) panic("decref pc=%#p", getcallerpc(&r)); return x; } /* * Rather than strncpy, which zeros the rest of the buffer, kstrcpy * truncates if necessary, always zero terminates, does not zero fill, * and puts ... at the end of the string if it's too long. Usually used to * save a string in up->genbuf; */ void kstrcpy(char *s, char *t, int ns) { int nt; nt = strlen(t); if(nt+1 <= ns){ memmove(s, t, nt+1); return; } /* too long */ if(ns < 4){ /* but very short! */ strncpy(s, t, ns); return; } /* truncate with ... at character boundary (very rare case) */ memmove(s, t, ns-4); ns -= 4; s[ns] = '\0'; /* look for first byte of UTF-8 sequence by skipping continuation bytes */ while(ns>0 && (s[--ns]&0xC0)==0x80) ; strcpy(s+ns, "..."); } int emptystr(char *s) { if(s == nil) return 1; if(s[0] == '\0') return 1; return 0; } /* * Atomically replace *p with copy of s */ void kstrdup(char **p, char *s) { int n; char *t, *prev; static Lock l; n = strlen(s)+1; /* if it's a user, we can wait for memory; if not, something's very wrong */ if(up){ t = smalloc(n); setmalloctag(t, getcallerpc(&p)); }else{ t = malloc(n); if(t == nil) panic("kstrdup: no memory"); } memmove(t, s, n); prev = *p; *p = t; free(prev); } void chandevreset(int sw) { int i; for(i=0; devtab[i] != nil; i++) if((sw != 0 && i >= bootdevtabs) || (sw == 0 && i < bootdevtabs)) devtab[i]->reset(); } void chandevinit(int sw) { int i; todinit(); /* avoid later reentry causing infinite recursion */ for(i=0; devtab[i] != nil; i++) if((sw != 0 && i >= bootdevtabs) || (sw == 0 && i < bootdevtabs)) devtab[i]->init(); } void chandevshutdown(void) { int i; /* shutdown in reverse order */ for(i=0; devtab[i] != nil; i++) ; for(i--; i >= 0; i--) devtab[i]->shutdown(); } Chan* newchan(void) { Chan *c; lock(&chanalloc); c = chanalloc.free; if(c != 0) chanalloc.free = c->next; unlock(&chanalloc); if(c == nil){ c = smalloc(sizeof(Chan)); lock(&chanalloc); c->fid = ++chanalloc.fid; c->link = chanalloc.list; chanalloc.list = c; unlock(&chanalloc); } /* if you get an error before associating with a dev, close calls rootclose, a nop */ c->type = 0; c->flag = 0; c->ref = 1; c->dev = 0; c->offset = 0; c->devoffset = 0; c->iounit = 0; c->umh = 0; c->uri = 0; c->dri = 0; c->aux = 0; c->mchan = 0; c->mcp = 0; c->mux = 0; memset(&c->mqid, 0, sizeof(c->mqid)); c->path = 0; c->ismtpt = 0; return c; } Ref npath; Path* newpath(char *s) { int i; Path *p; p = smalloc(sizeof(Path)); i = strlen(s); p->len = i; p->alen = i+PATHSLOP; p->s = smalloc(p->alen); memmove(p->s, s, i+1); p->ref = 1; incref(&npath); /* * Cannot use newpath for arbitrary names because the mtpt * array will not be populated correctly. The names #/ and / are * allowed, but other names with / in them draw warnings. */ if(strchr(s, '/') && strcmp(s, "#/") != 0 && strcmp(s, "/") != 0) print("newpath: %s from %#p\n", s, getcallerpc(&s)); p->mlen = 1; p->malen = PATHMSLOP; p->mtpt = smalloc(p->malen*sizeof p->mtpt[0]); return p; } static Path* copypath(Path *p) { int i; Path *pp; pp = smalloc(sizeof(Path)); pp->ref = 1; incref(&npath); DBG("copypath %s %p => %p\n", p->s, p, pp); pp->len = p->len; pp->alen = p->alen; pp->s = smalloc(p->alen); memmove(pp->s, p->s, p->len+1); pp->mlen = p->mlen; pp->malen = p->malen; pp->mtpt = smalloc(p->malen*sizeof pp->mtpt[0]); for(i=0; imlen; i++){ pp->mtpt[i] = p->mtpt[i]; if(pp->mtpt[i]) incref(pp->mtpt[i]); } return pp; } void pathclose(Path *p) { int i; if(p == nil) return; //XXX DBG("pathclose %p %s ref=%ld =>", p, p->s, p->ref); for(i=0; imlen; i++) DBG(" %p", p->mtpt[i]); DBG("\n"); if(decref(p)) return; decref(&npath); free(p->s); for(i=0; imlen; i++) if(p->mtpt[i]) cclose(p->mtpt[i]); free(p->mtpt); free(p); } /* * In place, rewrite name to compress multiple /, eliminate ., and process .. * (Really only called to remove a trailing .. that has been added. * Otherwise would need to update n->mtpt as well.) */ static void fixdotdotname(Path *p) { char *r; if(p->s[0] == '#'){ r = strchr(p->s, '/'); if(r == nil) return; cleanname(r); /* * The correct name is #i rather than #i/, * but the correct name of #/ is #/. */ if(strcmp(r, "/")==0 && p->s[1] != '/') *r = '\0'; }else cleanname(p->s); p->len = strlen(p->s); } static Path* uniquepath(Path *p) { Path *new; if(p->ref > 1){ /* copy on write */ new = copypath(p); pathclose(p); p = new; } return p; } static Path* addelem(Path *p, char *s, Chan *from) { char *t; int a, i; Chan *c, **tt; if(s[0]=='.' && s[1]=='\0') return p; p = uniquepath(p); i = strlen(s); if(p->len+1+i+1 > p->alen){ a = p->len+1+i+1 + PATHSLOP; t = smalloc(a); memmove(t, p->s, p->len+1); free(p->s); p->s = t; p->alen = a; } /* don't insert extra slash if one is present */ if(p->len>0 && p->s[p->len-1]!='/' && s[0]!='/') p->s[p->len++] = '/'; memmove(p->s+p->len, s, i+1); p->len += i; if(isdotdot(s)){ fixdotdotname(p); DBG("addelem %s .. => rm %p\n", p->s, p->mtpt[p->mlen-1]); if(p->mlen>1 && (c = p->mtpt[--p->mlen])){ p->mtpt[p->mlen] = nil; cclose(c); } }else{ if(p->mlen >= p->malen){ p->malen = p->mlen+1+PATHMSLOP; tt = smalloc(p->malen*sizeof tt[0]); memmove(tt, p->mtpt, p->mlen*sizeof tt[0]); free(p->mtpt); p->mtpt = tt; } DBG("addelem %s %s => add %p\n", p->s, s, from); p->mtpt[p->mlen++] = from; if(from) incref(from); } return p; } void chanfree(Chan *c) { c->flag = CFREE; if(c->dirrock != nil){ free(c->dirrock); c->dirrock = 0; c->nrock = 0; c->mrock = 0; } if(c->umh != nil){ putmhead(c->umh); c->umh = nil; } if(c->umc != nil){ cclose(c->umc); c->umc = nil; } if(c->mux != nil){ muxclose(c->mux); c->mux = nil; } if(c->mchan != nil){ cclose(c->mchan); c->mchan = nil; } pathclose(c->path); c->path = nil; lock(&chanalloc); c->next = chanalloc.free; chanalloc.free = c; unlock(&chanalloc); } void cclose(Chan *c) { if(c->flag&CFREE) panic("cclose %#p", getcallerpc(&c)); DBG("cclose %p name=%s ref=%ld\n", c, c->path->s, c->ref); if(decref(c)) return; if(!waserror()){ devtab[c->type]->close(c); poperror(); } chanfree(c); } /* * Queue a chan to be closed by one of the clunk procs. */ struct { Chan *head; Chan *tail; int nqueued; int nclosed; Lock l; QLock q; Rendez r; } clunkq; void closeproc(void*); void ccloseq(Chan *c) { if(c->flag&CFREE) panic("cclose %#p", getcallerpc(&c)); DBG("ccloseq %p name=%s ref=%ld\n", c, c->path->s, c->ref); if(decref(c)) return; lock(&clunkq.l); clunkq.nqueued++; c->next = nil; if(clunkq.head) clunkq.tail->next = c; else clunkq.head = c; clunkq.tail = c; unlock(&clunkq.l); if(!wakeup(&clunkq.r)) kproc("closeproc", closeproc, nil); } static int clunkwork(void*) { return clunkq.head != nil; } void closeproc(void*) { Chan *c; for(;;){ qlock(&clunkq.q); if(clunkq.head == nil){ if(!waserror()){ tsleep(&clunkq.r, clunkwork, nil, 5000); poperror(); } if(clunkq.head == nil){ qunlock(&clunkq.q); pexit("no work", 1); } } lock(&clunkq.l); c = clunkq.head; clunkq.head = c->next; clunkq.nclosed++; unlock(&clunkq.l); qunlock(&clunkq.q); if(!waserror()){ devtab[c->type]->close(c); poperror(); } chanfree(c); } } /* * Make sure we have the only copy of c. (Copy on write.) */ Chan* cunique(Chan *c) { Chan *nc; if(c->ref != 1){ nc = cclone(c); cclose(c); c = nc; } return c; } int eqqid(Qid a, Qid b) { return a.path==b.path && a.vers==b.vers; } int eqchan(Chan *a, Chan *b, int skipvers) { if(a->qid.path != b->qid.path) return 0; if(!skipvers && a->qid.vers!=b->qid.vers) return 0; if(a->type != b->type) return 0; if(a->dev != b->dev) return 0; return 1; } int eqchantdqid(Chan *a, int type, int dev, Qid qid, int skipvers) { if(a->qid.path != qid.path) return 0; if(!skipvers && a->qid.vers!=qid.vers) return 0; if(a->type != type) return 0; if(a->dev != dev) return 0; return 1; } Mhead* newmhead(Chan *from) { Mhead *mh; mh = smalloc(sizeof(Mhead)); mh->ref = 1; mh->from = from; incref(from); return mh; } int cmount(Chan **newp, Chan *old, int flag, char *spec) { int order, flg; Chan *new; Mhead *m, **l, *mh; Mount *nm, *f, *um, **h; Pgrp *pg; if(QTDIR & (old->qid.type^(*newp)->qid.type)) error(Emount); if(old->umh) print("cmount: unexpected umh, caller %#p\n", getcallerpc(&newp)); order = flag&MORDER; if((old->qid.type&QTDIR)==0 && order != MREPL) error(Emount); new = *newp; mh = new->umh; /* * Not allowed to bind when the old directory is itself a union. * (Maybe it should be allowed, but I don't see what the semantics * would be.) * * We need to check mh->mount->next to tell unions apart from * simple mount points, so that things like * mount -c fd /root * bind -c /root / * work. * * The check of mount->mflag allows things like * mount fd /root * bind -c /root / * * This is far more complicated than it should be, but I don't * see an easier way at the moment. */ if((flag&MCREATE) && mh && mh->mount && (mh->mount->next || !(mh->mount->mflag&MCREATE))) error(Emount); pg = up->pgrp; wlock(&pg->ns); l = &MOUNTH(pg, old->qid); for(m = *l; m; m = m->hash){ if(eqchan(m->from, old, 1)) break; l = &m->hash; } if(m == nil){ /* * nothing mounted here yet. create a mount * head and add to the hash table. */ m = newmhead(old); *l = m; /* * if this is a union mount, add the old * node to the mount chain. */ if(order != MREPL) m->mount = newmount(m, old, 0, 0); } wlock(&m->lock); if(waserror()){ wunlock(&m->lock); nexterror(); } wunlock(&pg->ns); nm = newmount(m, new, flag, spec); if(mh != nil && mh->mount != nil){ /* * copy a union when binding it onto a directory */ flg = order; if(order == MREPL) flg = MAFTER; h = &nm->next; um = mh->mount; for(um = um->next; um; um = um->next){ f = newmount(m, um->to, flg, um->spec); *h = f; h = &f->next; } } if(m->mount && order == MREPL){ mountfree(m->mount); m->mount = 0; } if(flag & MCREATE) nm->mflag |= MCREATE; if(m->mount && order == MAFTER){ for(f = m->mount; f->next; f = f->next) ; f->next = nm; }else{ for(f = nm; f->next; f = f->next) ; f->next = m->mount; m->mount = nm; } wunlock(&m->lock); poperror(); return nm->mountid; } void cunmount(Chan *mnt, Chan *mounted) { Pgrp *pg; Mhead *m, **l; Mount *f, **p; if(mnt->umh) /* should not happen */ print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); /* * It _can_ happen that mounted->umh is non-nil, * because mounted is the result of namec(Aopen) * (see sysfile.c:/^sysunmount). * If we open a union directory, it will have a umh. * Although surprising, this is okay, since the * cclose will take care of freeing the umh. */ pg = up->pgrp; wlock(&pg->ns); l = &MOUNTH(pg, mnt->qid); for(m = *l; m; m = m->hash){ if(eqchan(m->from, mnt, 1)) break; l = &m->hash; } if(m == 0){ wunlock(&pg->ns); error(Eunmount); } wlock(&m->lock); if(mounted == 0){ *l = m->hash; wunlock(&pg->ns); mountfree(m->mount); m->mount = nil; cclose(m->from); wunlock(&m->lock); putmhead(m); return; } p = &m->mount; for(f = *p; f; f = f->next){ /* BUG: Needs to be 2 pass */ if(eqchan(f->to, mounted, 1) || (f->to->mchan && eqchan(f->to->mchan, mounted, 1))){ *p = f->next; f->next = 0; mountfree(f); if(m->mount == nil){ *l = m->hash; cclose(m->from); wunlock(&m->lock); wunlock(&pg->ns); putmhead(m); return; } wunlock(&m->lock); wunlock(&pg->ns); return; } p = &f->next; } wunlock(&m->lock); wunlock(&pg->ns); error(Eunion); } Chan* cclone(Chan *c) { Chan *nc; Walkqid *wq; wq = devtab[c->type]->walk(c, nil, nil, 0); if(wq == nil) error("clone failed"); nc = wq->clone; free(wq); nc->path = c->path; if(c->path) incref(c->path); return nc; } /* also used by sysfile.c:/^mountfix */ int findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) { Pgrp *pg; Mhead *m; pg = up->pgrp; rlock(&pg->ns); for(m = MOUNTH(pg, qid); m; m = m->hash){ rlock(&m->lock); if(m->from == nil){ print("m %p m->from 0\n", m); runlock(&m->lock); continue; } if(eqchantdqid(m->from, type, dev, qid, 1)){ runlock(&pg->ns); if(mp != nil){ incref(m); if(*mp != nil) putmhead(*mp); *mp = m; } if(*cp != nil) cclose(*cp); incref(m->mount->to); *cp = m->mount->to; runlock(&m->lock); return 1; } runlock(&m->lock); } runlock(&pg->ns); return 0; } /* * Calls findmount but also updates path. */ static int domount(Chan **cp, Mhead **mp, Path **path) { Chan **lc; Path *p; if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0) return 0; if(path){ p = *path; p = uniquepath(p); if(p->mlen <= 0) print("domount: path %s has mlen==%d\n", p->s, p->mlen); else{ lc = &p->mtpt[p->mlen-1]; DBG("domount %p %s => add %p (was %p)\n", p, p->s, (*mp)->from, p->mtpt[p->mlen-1]); incref((*mp)->from); if(*lc) cclose(*lc); *lc = (*mp)->from; } *path = p; } return 1; } /* * If c is the right-hand-side of a mount point, returns the left hand side. * Changes name to reflect the fact that we've uncrossed the mountpoint, * so name had better be ours to change! */ static Chan* undomount(Chan *c, Path *path) { Chan *nc; if(path->ref != 1 || path->mlen == 0) print("undomount: path %s ref %ld mlen %d caller %#p\n", path->s, path->ref, path->mlen, getcallerpc(&c)); if(path->mlen>0 && (nc=path->mtpt[path->mlen-1]) != nil){ DBG("undomount %p %s => remove %p\n", path, path->s, nc); cclose(c); path->mtpt[path->mlen-1] = nil; c = nc; } return c; } /* * Call dev walk but catch errors. */ static Walkqid* ewalk(Chan *c, Chan *nc, char **name, int nname) { Walkqid *wq; if(waserror()) return nil; wq = devtab[c->type]->walk(c, nc, name, nname); poperror(); return wq; } /* * Either walks all the way or not at all. No partial results in *cp. * *nerror is the number of names to display in an error message. */ static char Edoesnotexist[] = "does not exist"; int walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) { int dev, didmount, dotdot, i, n, nhave, ntry, type; Chan *c, *nc, *mtpt; Path *path; Mhead *mh, *nmh; Mount *f; Walkqid *wq; c = *cp; incref(c); path = c->path; incref(path); mh = nil; /* * While we haven't gotten all the way down the path: * 1. step through a mount point, if any * 2. send a walk request for initial dotdot or initial prefix without dotdot * 3. move to the first mountpoint along the way. * 4. repeat. * * Each time through the loop: * * If didmount==0, c is on the undomount side of the mount point. * If didmount==1, c is on the domount side of the mount point. * Either way, c's full path is path. */ didmount = 0; for(nhave=0; nhaveqid.type&QTDIR)==0){ if(nerror) *nerror = nhave; pathclose(path); cclose(c); strcpy(up->errstr, Enotdir); if(mh != nil) putmhead(mh); return -1; } ntry = nnames - nhave; if(ntry > MAXWELEM) ntry = MAXWELEM; dotdot = 0; for(i=0; itype; dev = c->dev; if((wq = ewalk(c, nil, names+nhave, ntry)) == nil){ /* try a union mount, if any */ if(mh && !nomount){ /* * mh->mount->to == c, so start at mh->mount->next */ rlock(&mh->lock); for(f = mh->mount->next; f; f = f->next) if((wq = ewalk(f->to, nil, names+nhave, ntry)) != nil) break; runlock(&mh->lock); if(f != nil){ type = f->to->type; dev = f->to->dev; } } if(wq == nil){ cclose(c); pathclose(path); if(nerror) *nerror = nhave+1; if(mh != nil) putmhead(mh); return -1; } } didmount = 0; if(dotdot){ assert(wq->nqid == 1); assert(wq->clone != nil); path = addelem(path, "..", nil); nc = undomount(wq->clone, path); nmh = nil; n = 1; }else{ nc = nil; nmh = nil; if(!nomount){ for(i=0; inqid && iqid[i])){ didmount = 1; break; } } } if(nc == nil){ /* no mount points along path */ if(wq->clone == nil){ cclose(c); pathclose(path); if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ if(nerror) *nerror = nhave+wq->nqid+1; strcpy(up->errstr, Edoesnotexist); }else{ if(nerror) *nerror = nhave+wq->nqid; strcpy(up->errstr, Enotdir); } free(wq); if(mh != nil) putmhead(mh); return -1; } n = wq->nqid; nc = wq->clone; }else{ /* stopped early, at a mount point */ didmount = 1; if(wq->clone != nil){ cclose(wq->clone); wq->clone = nil; } n = i+1; } for(i=0; ifrom; path = addelem(path, names[nhave+i], mtpt); } } cclose(c); c = nc; putmhead(mh); mh = nmh; free(wq); } putmhead(mh); c = cunique(c); if(c->umh != nil){ //BUG print("walk umh\n"); putmhead(c->umh); c->umh = nil; } pathclose(c->path); c->path = path; cclose(*cp); *cp = c; if(nerror) *nerror = nhave; return 0; } /* * c is a mounted non-creatable directory. find a creatable one. */ Chan* createdir(Chan *c, Mhead *m) { Chan *nc; Mount *f; rlock(&m->lock); if(waserror()){ runlock(&m->lock); nexterror(); } for(f = m->mount; f; f = f->next){ if(f->mflag&MCREATE){ nc = cclone(f->to); runlock(&m->lock); poperror(); cclose(c); return nc; } } error(Enocreate); return 0; } void saveregisters(void) { } static void growparse(Elemlist *e) { char **new; int *inew; enum { Delta = 8 }; if(e->nelems % Delta == 0){ new = smalloc((e->nelems+Delta) * sizeof(char*)); memmove(new, e->elems, e->nelems*sizeof(char*)); free(e->elems); e->elems = new; inew = smalloc((e->nelems+Delta+1) * sizeof(int)); memmove(inew, e->off, (e->nelems+1)*sizeof(int)); free(e->off); e->off = inew; } } /* * The name is known to be valid. * Copy the name so slashes can be overwritten. * An empty string will set nelem=0. * A path ending in / or /. or /.//./ etc. will have * e.mustbedir = 1, so that we correctly * reject, e.g., "/adm/users/." when /adm/users is a file * rather than a directory. */ static void parsename(char *aname, Elemlist *e) { char *name, *slash; kstrdup(&e->name, aname); name = e->name; e->nelems = 0; e->elems = nil; e->off = smalloc(sizeof(int)); e->off[0] = skipslash(name) - name; for(;;){ name = skipslash(name); if(*name == '\0'){ e->off[e->nelems] = name+strlen(name) - e->name; e->mustbedir = 1; break; } growparse(e); e->elems[e->nelems++] = name; slash = utfrune(name, '/'); if(slash == nil){ e->off[e->nelems] = name+strlen(name) - e->name; e->mustbedir = 0; break; } e->off[e->nelems] = slash - e->name; *slash++ = '\0'; name = slash; } if(0 && chandebug){ int i; print("parsename %s:", e->name); for(i=0; i<=e->nelems; i++) print(" %d", e->off[i]); print("\n"); } } void* memrchr(void *va, int c, long n) { uchar *a, *e; a = va; for(e=a+n-1; e>a; e--) if(*e == c) return e; return nil; } void namelenerror(char *aname, int len, char *err) { char *ename, *name, *next; int i, errlen; /* * If the name is short enough, just use the whole thing. */ errlen = strlen(err); if(len < ERRMAX/3 || len+errlen < 2*ERRMAX/3) snprint(up->genbuf, sizeof up->genbuf, "%.*s", utfnlen(aname, len), aname); else{ /* * Print a suffix of the name, but try to get a little info. */ ename = aname+len; next = ename; do{ name = next; next = memrchr(aname, '/', name-aname); if(next == nil) next = aname; len = ename-next; }while(len < ERRMAX/3 || len + errlen < 2*ERRMAX/3); /* * If the name is ridiculously long, chop it. */ if(name == ename){ name = ename-ERRMAX/4; if(name <= aname) panic("bad math in namelenerror"); /* walk out of current UTF sequence */ for(i=0; (*name&0xC0)==0x80 && i<3; i++) name++; } snprint(up->genbuf, sizeof up->genbuf, "...%.*s", utfnlen(name, ename-name), name); } snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err); nexterror(); } void nameerror(char *name, char *err) { namelenerror(name, strlen(name), err); } /* * Turn a name into a channel. * &name[0] is known to be a valid address. It may be a kernel address. * * Opening with amode Aopen, Acreate, Aremove, or Aaccess guarantees * that the result will be the only reference to that particular fid. * This is necessary since we might pass the result to * devtab[]->remove(). * * Opening Atodir or Amount does not guarantee this. * * Under certain circumstances, opening Aaccess will cause * an unnecessary clone in order to get a cunique Chan so it * can attach the correct name. Sysstat and sys_stat need the * correct name so they can rewrite the stat info. */ Chan* namec(char *aname, int amode, int omode, ulong perm) { int len, n, t, nomount; Chan *c, *cnew; Path *path; Elemlist e; Rune r; Mhead *m; char *createerr, tmperrbuf[ERRMAX]; char *name; if(aname[0] == '\0') error("empty file name"); aname = validnamedup(aname, 1); if(waserror()){ free(aname); nexterror(); } DBG("namec %s %d %d\n", aname, amode, omode); name = aname; /* * Find the starting off point (the current slash, the root of * a device tree, or the current dot) as well as the name to * evaluate starting there. */ nomount = 0; switch(name[0]){ case '/': c = up->slash; incref(c); break; case '#': nomount = 1; up->genbuf[0] = '\0'; n = 0; while(*name != '\0' && (*name != '/' || n < 2)){ if(n >= sizeof(up->genbuf)-1) error(Efilename); up->genbuf[n++] = *name++; } up->genbuf[n] = '\0'; /* * noattach is sandboxing. * * the OK exceptions are: * | it only gives access to pipes you create * d this process's file descriptors * e this process's environment * the iffy exceptions are: * c time and pid, but also cons and consctl * p control of your own processes (and unfortunately * any others left unprotected) */ n = chartorune(&r, up->genbuf+1)+1; /* actually / is caught by parsing earlier */ if(utfrune("M", r)) error(Enoattach); if(up->pgrp->noattach && utfrune("|decp", r)==nil) error(Enoattach); t = devno(r, 1); if(t == -1) error(Ebadsharp); c = devtab[t]->attach(up->genbuf+n); break; default: c = up->dot; incref(c); break; } e.aname = aname; e.prefix = name - aname; e.name = nil; e.elems = nil; e.off = nil; e.nelems = 0; e.nerror = 0; if(waserror()){ cclose(c); free(e.name); free(e.elems); /* * Prepare nice error, showing first e.nerror elements of name. */ if(e.nerror == 0) nexterror(); strcpy(tmperrbuf, up->errstr); if(e.off[e.nerror]==0) print("nerror=%d but off=%d\n", e.nerror, e.off[e.nerror]); if(0 && chandebug) print("showing %d+%d/%d (of %d) of %s (%d %d)\n", e.prefix, e.off[e.nerror], e.nerror, e.nelems, aname, e.off[0], e.off[1]); len = e.prefix+e.off[e.nerror]; free(e.off); namelenerror(aname, len, tmperrbuf); } /* * Build a list of elements in the name. */ parsename(name, &e); /* * On create, .... */ if(amode == Acreate){ /* perm must have DMDIR if last element is / or /. */ if(e.mustbedir && !(perm&DMDIR)){ e.nerror = e.nelems; error("create without DMDIR"); } /* don't try to walk the last path element just yet. */ if(e.nelems == 0) error(Eexist); e.nelems--; } if(walk(&c, e.elems, e.nelems, nomount, &e.nerror) < 0){ if(e.nerror < 0 || e.nerror > e.nelems){ print("namec %s walk error nerror=%d\n", aname, e.nerror); e.nerror = 0; } nexterror(); } if(e.mustbedir && !(c->qid.type&QTDIR)) error("not a directory"); if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)) error("cannot exec directory"); switch(amode){ case Abind: /* no need to maintain path - cannot dotdot an Abind */ m = nil; if(!nomount) domount(&c, &m, nil); if(c->umh != nil) putmhead(c->umh); c->umh = m; break; case Aaccess: case Aremove: case Aopen: Open: /* save&update the name; domount might change c */ path = c->path; incref(path); m = nil; if(!nomount) domount(&c, &m, &path); /* our own copy to open or remove */ c = cunique(c); /* now it's our copy anyway, we can put the name back */ pathclose(c->path); c->path = path; /* record whether c is on a mount point */ c->ismtpt = m!=nil; switch(amode){ case Aaccess: case Aremove: putmhead(m); break; case Aopen: case Acreate: if(c->umh != nil){ print("cunique umh Open\n"); putmhead(c->umh); c->umh = nil; } /* only save the mount head if it's a multiple element union */ if(m && m->mount && m->mount->next) c->umh = m; else putmhead(m); /* save registers else error() in open has wrong value of c saved */ saveregisters(); if(omode == OEXEC) c->flag &= ~CCACHE; c = devtab[c->type]->open(c, omode&~OCEXEC); if(omode & OCEXEC) c->flag |= CCEXEC; if(omode & ORCLOSE) c->flag |= CRCLOSE; break; } break; case Atodir: /* * Directories (e.g. for cd) are left before the mount point, * so one may mount on / or . and see the effect. */ if(!(c->qid.type & QTDIR)) error(Enotdir); break; case Amount: /* * When mounting on an already mounted upon directory, * one wants subsequent mounts to be attached to the * original directory, not the replacement. Don't domount. */ break; case Acreate: /* * We've already walked all but the last element. * If the last exists, try to open it OTRUNC. * If omode&OEXCL is set, just give up. */ e.nelems++; e.nerror++; if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ if(omode&OEXCL) error(Eexist); omode |= OTRUNC; goto Open; } /* * The semantics of the create(2) system call are that if the * file exists and can be written, it is to be opened with truncation. * On the other hand, the create(5) message fails if the file exists. * If we get two create(2) calls happening simultaneously, * they might both get here and send create(5) messages, but only * one of the messages will succeed. To provide the expected create(2) * semantics, the call with the failed message needs to try the above * walk again, opening for truncation. This correctly solves the * create/create race, in the sense that any observable outcome can * be explained as one happening before the other. * The create/create race is quite common. For example, it happens * when two rc subshells simultaneously update the same * environment variable. * * The implementation still admits a create/create/remove race: * (A) walk to file, fails * (B) walk to file, fails * (A) create file, succeeds, returns * (B) create file, fails * (A) remove file, succeeds, returns * (B) walk to file, return failure. * * This is hardly as common as the create/create race, and is really * not too much worse than what might happen if (B) got a hold of a * file descriptor and then the file was removed -- either way (B) can't do * anything with the result of the create call. So we don't care about this race. * * Applications that care about more fine-grained decision of the races * can use the OEXCL flag to get at the underlying create(5) semantics; * by default we provide the common case. * * We need to stay behind the mount point in case we * need to do the first walk again (should the create fail). * * We also need to cross the mount point and find the directory * in the union in which we should be creating. * * The channel staying behind is c, the one moving forward is cnew. */ m = nil; cnew = nil; /* is this assignment necessary? */ if(!waserror()){ /* try create */ if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) cnew = createdir(cnew, m); else{ cnew = c; incref(cnew); } /* * We need our own copy of the Chan because we're * about to send a create, which will move it. Once we have * our own copy, we can fix the name, which might be wrong * if findmount gave us a new Chan. */ cnew = cunique(cnew); pathclose(cnew->path); cnew->path = c->path; incref(cnew->path); devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); poperror(); if(omode & OCEXEC) cnew->flag |= CCEXEC; if(omode & ORCLOSE) cnew->flag |= CRCLOSE; if(m) putmhead(m); cclose(c); c = cnew; c->path = addelem(c->path, e.elems[e.nelems-1], nil); break; } /* create failed */ cclose(cnew); if(m) putmhead(m); if(omode & OEXCL) nexterror(); /* save error */ createerr = up->errstr; up->errstr = tmperrbuf; /* note: we depend that walk does not error */ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ up->errstr = createerr; error(createerr); /* report true error */ } up->errstr = createerr; omode |= OTRUNC; goto Open; default: panic("unknown namec access %d\n", amode); } /* place final element in genbuf for e.g. exec */ if(e.nelems > 0) kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); else kstrcpy(up->genbuf, ".", sizeof up->genbuf); free(e.name); free(e.elems); free(e.off); poperror(); /* e c */ free(aname); poperror(); /* aname */ return c; } /* * name is valid. skip leading / and ./ as much as possible */ char* skipslash(char *name) { while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) name++; return name; } char isfrog[256]={ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, ['/'] 1, [0x7f] 1, }; /* * Check that the name * a) is in valid memory. * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. * c) contains no frogs. * The first byte is known to be addressible by the requester, so the * routine works for kernel and user memory both. * The parameter slashok flags whether a slash character is an error * or a valid character. * * The parameter dup flags whether the string should be copied * out of user space before being scanned the second time. * (Otherwise a malicious thread could remove the NUL, causing us * to access unchecked addresses.) */ static char* validname0(char *aname, int slashok, int dup, ulong pc) { char *ename, *name, *s; int c, n; Rune r; name = aname; if((ulong)name < KZERO){ validaddr((ulong)name, 1, 0); if(!dup) print("warning: validname called from %lux with user pointer", pc); ename = vmemchr(name, 0, (1<<16)); }else ename = memchr(name, 0, (1<<16)); if(ename==nil || ename-name>=(1<<16)) error("name too long"); s = nil; if(dup){ n = ename-name; s = smalloc(n+1); memmove(s, name, n); s[n] = 0; aname = s; name = s; setmalloctag(s, pc); } while(*name){ /* all characters above '~' are ok */ c = *(uchar*)name; if(c >= Runeself) name += chartorune(&r, name); else{ if(isfrog[c]) if(!slashok || c!='/'){ snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); free(s); error(up->genbuf); } name++; } } return s; } void validname(char *aname, int slashok) { validname0(aname, slashok, 0, getcallerpc(&aname)); } char* validnamedup(char *aname, int slashok) { return validname0(aname, slashok, 1, getcallerpc(&aname)); } void isdir(Chan *c) { if(c->qid.type & QTDIR) return; error(Enotdir); } /* * This is necessary because there are many * pointers to the top of a given mount list: * * - the mhead in the namespace hash table * - the mhead in chans returned from findmount: * used in namec and then by unionread. * - the mhead in chans returned from createdir: * used in the open/create race protect, which is gone. * * The RWlock in the Mhead protects the mount list it contains. * The mount list is deleted when we cunmount. * The RWlock ensures that nothing is using the mount list at that time. * * It is okay to replace c->mh with whatever you want as * long as you are sure you have a unique reference to it. * * This comment might belong somewhere else. */ void putmhead(Mhead *m) { if(m && decref(m) == 0){ m->mount = (Mount*)0xCafeBeef; free(m); } } rror(Enoattach); if(up->pgrp->noattach && utfrune("|decp", r)==nil) error(Enoattach); t = devno(r, 1); if(t == -1) error(Ebadsharp); c = devtab[t]->attach(up->genbuf+n); break; default: c = up->dot; incref(c); break; } e.aname = ana9am/9port/initcode.c 664 0 0 1566 11334032360 12374ustar00nemosys/* * IMPORTANT! DO NOT ADD LIBRARY CALLS TO THIS FILE. * The entire text image must fit on one page * (and there's no data segment, so any read/write data must be on the stack). */ #include #include char cons[] = "#c/cons"; char p9am[] = "/boot/9am"; char boot[] = "/boot/boot"; char dev[] = "/dev"; char c[] = "#c"; char e[] = "#e"; char ec[] = "#ec"; char s[] = "#s"; char srv[] = "/srv"; char env[] = "/env"; void startboot(char *argv0, char **argv) { char buf[200]; /* keep this fairly large to capture error details */ /* in case boot is a shell script */ open(cons, OREAD); open(cons, OWRITE); open(cons, OWRITE); bind(c, dev, MAFTER); bind(ec, env, MAFTER); bind(e, env, MCREATE|MAFTER); bind(s, srv, MREPL|MCREATE); argv[0] = boot; exec(p9am, argv); exec(boot, argv); rerrstr(buf, sizeof buf); buf[sizeof buf - 1] = '\0'; _exits(buf); } ec directory"); switch(amode){ case Abind: /* no need to maintain path - cannot dotdot an Abind */ m = nil; if(!nomount) domou9am/9port/mkdevc 775 0 0 10456 11334032360 11647ustar00nemosys#!/bin/rc awk -v 'objtype='$objtype -v 'pwd='`{pwd} ' BEGIN{ if(ARGC < 2) exit dev[ndev++] = "root"; dev[ndev++] = "env"; bootdevtabs = ndev; } /^[ \t]*$/{ next; } /^#/{ next; } collect && /^[^ \t]/{ collect = 0; } collect && section == "dev"{ for(i = 0; i < ndev; i++) if(dev[i] == $1) break; if(i == ndev) dev[ndev++] = $1; if($1 == "ad") devad = 1; else if($1 == "sd") devsd = 1; else if($1 == "uart") devuart = 1; else if($1 == "vga") devvga = 1; for(i = 2; i <= NF; i++){ if($i == "dma" && objtype ~ "(386|alpha|amd64)") i8237dma++; } } collect && section == "ip"{ ip[nip++] = $1; } collect && section == "link"{ link[nlink++] = $1; } collect && section == "misc"{ sub(/^[ \t]*/, ""); misc[nmisc++] = $1; if($1 ~ "^arch.*") arch[narch++] = $1; else if($1 ~ "^ad.*") adifc[nadifc++] = $1; else if($1 ~ "^sd.*") sdifc[nsdifc++] = $1; else if($1 ~ "^uart.*") physuart[nphysuart++] = substr($1, 5, length($1)-4) "physuart"; else if($1 ~ "^vga.*"){ if(NF == 1) vgadev[nvgadev++] = $1; else for(i = 2; i <= NF; i++){ if($i ~ "[+]cur") vgadev[nvgadev++] = $1; if($i ~ "[+=]cur") vgacur[nvgacur++] = $1; } } } collect && section == "port"{ sub(/^[ \t]*/, ""); port[nport++] = $0; } $0 ~ /^[^ \t]/{ if($1 ~ "(bootdir|dev|ip|link|misc|port)"){ section = $1; collect = 1; } next; } END{ if(ARGC < 2) exit "usage" printf "#include \"u.h\"\n"; printf "#include \"../port/lib.h\"\n"; printf "#include \"mem.h\"\n"; printf "#include \"dat.h\"\n"; printf "#include \"fns.h\"\n"; printf "#include \"io.h\"\n"; printf "#include \"../port/error.h\"\n\n"; for(i = 0; i < ndev; i++) printf "extern Dev %sdevtab;\n", dev[i]; printf "int bootdevtabs=%d;\n", bootdevtabs; printf "Dev* devtab[]={\n"; for(i = 0; i < ndev; i++) printf "\t&%sdevtab,\n", dev[i]; printf "\tnil,\n};\n\n"; if(objtype ~ "(386|alpha|amd64)"){ alloc = "nil"; if(i8237dma){ printf "extern void _i8237alloc(void);\n"; alloc = "_i8237alloc"; } printf "void (*i8237alloc)(void) = %s;\n", alloc; printf "int i8237dma = %d;\n\n", i8237dma; } for(i = 0; i < nlink; i++) printf "extern void %slink(void);\n", link[i]; printf "void links(void){\n"; print "\tbootlinks();\n"; for(i = 0; i < nlink; i++) printf "\t%slink();\n", link[i]; printf "}\n\n"; if(narch || objtype == "386"){ for(i = 0; i < narch; i++) printf "extern PCArch %s;\n", arch[i]; printf "PCArch* knownarch[] = {\n"; for(i = 0; i < narch; i++) printf "\t&%s,\n", arch[i]; printf "\tnil,\n};\n\n"; } if(devad){ printf "#include \"../port/ad.h\"\n"; for(i = 0; i < nadifc; i++) printf "extern ADifc %sifc;\n", adifc[i]; printf "ADifc* adifc[] = {\n"; for(i = 0; i < nadifc; i++) printf "\t&%sifc,\n", adifc[i]; printf "\tnil,\n};\n\n"; } if(devsd){ printf "#include \"../port/sd.h\"\n"; for(i = 0; i < nsdifc; i++) printf "extern SDifc %sifc;\n", sdifc[i]; printf "SDifc* sdifc[] = {\n"; for(i = 0; i < nsdifc; i++) printf "\t&%sifc,\n", sdifc[i]; printf "\tnil,\n};\n\n"; } if(devuart){ for(i = 0; i < nphysuart; i++) printf "extern PhysUart %s;\n", physuart[i]; printf "PhysUart* physuart[] = {\n"; for(i = 0; i < nphysuart; i++) printf "\t&%s,\n", physuart[i]; printf "\tnil,\n};\n\n"; } if(devvga){ printf "#define Image IMAGE\n"; printf "#include \n"; printf "#include \n"; printf "#include \n"; printf "#include \"screen.h\"\n"; for(i = 0; i < nvgadev; i++) printf "extern VGAdev %sdev;\n", vgadev[i]; printf "VGAdev* vgadev[] = {\n"; for(i = 0; i < nvgadev; i++) printf "\t&%sdev,\n", vgadev[i]; printf "\tnil,\n};\n\n"; for(i = 0; i < nvgacur; i++) printf "extern VGAcur %scur;\n", vgacur[i]; printf "VGAcur* vgacur[] = {\n"; for(i = 0; i < nvgacur; i++) printf "\t&%scur,\n", vgacur[i]; printf "\tnil,\n};\n\n"; } if(nip){ printf "#include \"../ip/ip.h\"\n"; for(i = 0; i < nip; i++) printf "extern void %sinit(Fs*);\n", ip[i]; printf "void (*ipprotoinit[])(Fs*) = {\n"; for(i = 0; i < nip; i++) printf "\t%sinit,\n", ip[i]; printf "\tnil,\n};\n\n"; } if(nport){ for(i = 0; i < nport; i++) printf "%s\n", port[i]; printf "\n"; } printf "char* conffile = \"%s/%s\";\n", pwd, ARGV[1]; printf "ulong kerndate = KERNDATE;\n"; exit }' $* end that walk does not error */ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ up->errstr = createerr; error(createerr); /* report true error */ } up->errstr = createerr; omode |= OTRUNC; 9am/9port/mkrootc 775 0 0 1366 11334032360 12034ustar00nemosys#!/bin/rc rfork e n=`{echo $#*^'%3' | hoc} if(! ~ $n 0){ echo 'usage: mkrootc [name cname file]...' >[1=2] exit usage } tmp=mkroot.$pid.out fn sigexit { rm -f $tmp } allcname=() allname=() while(! ~ $#* 0){ name=$1 cname=$2 file=$3 shift shift shift allname=($allname $name) allcname=($allcname $cname) } echo ' #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" ' for(i in $allcname){ echo 'extern uchar '$i'code[];' echo 'extern ulong '$i'len;' } echo ' void bootlinks(void){ static int done; if(done++) return; ' x=($allname) for(i in $allcname){ name=$x(1) *=($x); shift; x=($*) echo ' addbootfile("'$name'", '$i'code, '$i'len);' } echo ' } ' ad could remove the NUL, causing us * to access unchecked addresses.) */ static char* validname0(char *aname, int slashok, int dup, ulong pc) { char *ename, *name, *s; int c, n; Rune r; name = aname; if((ulong)name < KZERO){ validaddr((ulong)name, 1, 0); 9am/9port/portdat.h 664 0 0 52615 11334032360 12301ustar00nemosystypedef struct Alarms Alarms; typedef struct Block Block; typedef struct Chan Chan; typedef struct Cmdbuf Cmdbuf; typedef struct Cmdtab Cmdtab; typedef struct Confmem Confmem; typedef struct Dev Dev; typedef struct Dirtab Dirtab; typedef struct Edf Edf; typedef struct Egrp Egrp; typedef struct Evalue Evalue; typedef struct Fgrp Fgrp; typedef struct DevConf DevConf; typedef struct Image Image; typedef struct Log Log; typedef struct Logflag Logflag; typedef struct Mntcache Mntcache; typedef struct Mount Mount; typedef struct Mntrpc Mntrpc; typedef struct Mntwalk Mntwalk; typedef struct Mnt Mnt; typedef struct Mhead Mhead; typedef struct Note Note; typedef struct Page Page; typedef struct Path Path; typedef struct Palloc Palloc; typedef struct Pallocmem Pallocmem; typedef struct Perf Perf; typedef struct PhysUart PhysUart; typedef struct Pgrp Pgrp; typedef struct Physseg Physseg; typedef struct Proc Proc; typedef struct Pte Pte; typedef struct QLock QLock; typedef struct Queue Queue; typedef struct Ref Ref; typedef struct Rendez Rendez; typedef struct Rgrp Rgrp; typedef struct RWlock RWlock; typedef struct Sargs Sargs; typedef struct Schedq Schedq; typedef struct Segment Segment; typedef struct Sema Sema; typedef struct Timer Timer; typedef struct Timers Timers; typedef struct Uart Uart; typedef struct Waitq Waitq; typedef struct Walkqid Walkqid; typedef struct Watchdog Watchdog; typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); #pragma incomplete DevConf #pragma incomplete Edf #pragma incomplete Mntcache #pragma incomplete Mntrpc #pragma incomplete Queue #pragma incomplete Timers #include struct Ref { Lock; long ref; }; struct Rendez { Lock; Proc *p; }; struct QLock { Lock use; /* to access Qlock structure */ Proc *head; /* next process waiting for object */ Proc *tail; /* last process waiting for object */ int locked; /* flag */ }; struct RWlock { Lock use; Proc *head; /* list of waiting processes */ Proc *tail; ulong wpc; /* pc of writer */ Proc *wproc; /* writing proc */ int readers; /* number of readers */ int writer; /* number of writers */ }; struct Alarms { QLock; Proc *head; }; struct Sargs { ulong args[MAXSYSARG]; }; /* * Access types in namec & channel flags */ enum { Aaccess, /* as in stat, wstat */ Abind, /* for left-hand-side of bind */ Atodir, /* as in chdir */ Aopen, /* for i/o */ Amount, /* to be mounted or mounted upon */ Acreate, /* is to be created */ Aremove, /* will be removed by caller */ COPEN = 0x0001, /* for i/o */ CMSG = 0x0002, /* the message channel for a mount */ /*rsc CCREATE = 0x0004, /* permits creation if c->mnt */ CCEXEC = 0x0008, /* close on exec */ CFREE = 0x0010, /* not in use */ CRCLOSE = 0x0020, /* remove on close */ CCACHE = 0x0080, /* client cache */ }; /* flag values */ enum { BINTR = (1<<0), BFREE = (1<<1), Bipck = (1<<2), /* ip checksum */ Budpck = (1<<3), /* udp checksum */ Btcpck = (1<<4), /* tcp checksum */ Bpktck = (1<<5), /* packet checksum */ }; struct Block { long ref; Block* next; Block* list; uchar* rp; /* first unconsumed byte */ uchar* wp; /* first empty byte */ uchar* lim; /* 1 past the end of the buffer */ uchar* base; /* start of the buffer */ void (*free)(Block*); ushort flag; ushort checksum; /* IP checksum of complete packet (minus media header) */ }; #define BLEN(s) ((s)->wp - (s)->rp) #define BALLOC(s) ((s)->lim - (s)->base) struct Chan { Ref; /* the Lock in this Ref is also Chan's lock */ Chan* next; /* allocation */ Chan* link; vlong offset; /* in fd */ vlong devoffset; /* in underlying device; see read */ ushort type; ulong dev; ushort mode; /* read/write */ ushort flag; Qid qid; int fid; /* for devmnt */ ulong iounit; /* chunk size for i/o; 0==default */ Mhead* umh; /* mount point that derived Chan; used in unionread */ Chan* umc; /* channel in union; held for union read */ QLock umqlock; /* serialize unionreads */ int uri; /* union read index */ int dri; /* devdirread index */ uchar* dirrock; /* directory entry rock for translations */ int nrock; int mrock; QLock rockqlock; int ismtpt; Mntcache*mcp; /* Mount cache pointer */ Mnt* mux; /* Mnt for clients using me for messages */ union { void* aux; Qid pgrpid; /* for #p/notepg */ ulong mid; /* for ns in devproc */ }; Chan* mchan; /* channel to mounted server */ Qid mqid; /* qid of root of mount point */ Path* path; }; struct Path { Ref; char *s; Chan **mtpt; /* mtpt history */ int len; /* strlen(s) */ int alen; /* allocated length of s */ int mlen; /* number of path elements */ int malen; /* allocated length of mtpt */ }; struct Dev { int dc; char* name; void (*reset)(void); void (*init)(void); void (*shutdown)(void); Chan* (*attach)(char*); Walkqid*(*walk)(Chan*, Chan*, char**, int); int (*stat)(Chan*, uchar*, int); Chan* (*open)(Chan*, int); void (*create)(Chan*, char*, int, ulong); void (*close)(Chan*); long (*read)(Chan*, void*, long, vlong); Block* (*bread)(Chan*, long, ulong); long (*write)(Chan*, void*, long, vlong); long (*bwrite)(Chan*, Block*, ulong); void (*remove)(Chan*); int (*wstat)(Chan*, uchar*, int); void (*power)(int); /* power mgt: power(1) => on, power (0) => off */ int (*config)(int, char*, DevConf*); /* returns nil on error */ }; struct Dirtab { char name[KNAMELEN]; Qid qid; vlong length; long perm; }; struct Walkqid { Chan *clone; int nqid; Qid qid[1]; }; enum { NSMAX = 1000, NSLOG = 7, NSCACHE = (1<ref; channels on this mount point incref(c->mchan) == Mnt.c */ Chan *c; /* Channel to file service */ Proc *rip; /* Reader in progress */ Mntrpc *queue; /* Queue of pending requests on this channel */ ulong id; /* Multiplexer id for channel check */ Mnt *list; /* Free list */ int flags; /* cache */ int msize; /* data + IOHDRSZ */ char *version; /* 9P version */ Queue *q; /* input queue */ }; enum { NUser, /* note provided externally */ NExit, /* deliver note quietly */ NDebug, /* print debug message */ }; struct Note { char msg[ERRMAX]; int flag; /* whether system posted it */ }; enum { PG_NOFLUSH = 0, PG_TXTFLUSH = 1, /* flush dcache and invalidate icache */ PG_DATFLUSH = 2, /* flush both i & d caches (UNUSED) */ PG_NEWCOL = 3, /* page has been recolored */ PG_MOD = 0x01, /* software modified bit */ PG_REF = 0x02, /* software referenced bit */ }; struct Page { Lock; ulong pa; /* Physical address in memory */ ulong va; /* Virtual address for user */ ulong daddr; /* Disc address on swap */ ushort ref; /* Reference count */ char modref; /* Simulated modify/reference bits */ char color; /* Cache coloring */ char cachectl[MAXMACH]; /* Cache flushing control for putmmu */ Image *image; /* Associated text or swap image */ Page *next; /* Lru free list */ Page *prev; Page *hash; /* Image hash chains */ }; struct Swapalloc { Lock; /* Free map lock */ int free; /* currently free swap pages */ uchar* swmap; /* Base of swap map in memory */ uchar* alloc; /* Round robin allocator */ uchar* last; /* Speed swap allocation */ uchar* top; /* Top of swap map */ Rendez r; /* Pager kproc idle sleep */ ulong highwater; /* Pager start threshold */ ulong headroom; /* Space pager frees under highwater */ }swapalloc; struct Image { Ref; Chan *c; /* channel to text file */ Qid qid; /* Qid for page cache coherence */ Qid mqid; Chan *mchan; ushort type; /* Device type of owning channel */ Segment *s; /* TEXT segment for image if running */ Image *hash; /* Qid hash chains */ Image *next; /* Free list */ int notext; /* no file associated */ }; struct Pte { Page *pages[PTEPERTAB]; /* Page map for this chunk of pte */ Page **first; /* First used entry */ Page **last; /* Last used entry */ }; /* Segment types */ enum { SG_TYPE = 07, /* Mask type of segment */ SG_TEXT = 00, SG_DATA = 01, SG_BSS = 02, SG_STACK = 03, SG_SHARED = 04, SG_PHYSICAL = 05, SG_RONLY = 0040, /* Segment is read only */ SG_CEXEC = 0100, /* Detach at exec */ }; #define PG_ONSWAP 1 #define onswap(s) (((ulong)s)&PG_ONSWAP) #define pagedout(s) (((ulong)s)==0 || onswap(s)) #define swapaddr(s) (((ulong)s)&~PG_ONSWAP) #define SEGMAXSIZE (SEGMAPSIZE*PTEMAPMEM) struct Physseg { ulong attr; /* Segment attributes */ char *name; /* Attach name */ ulong pa; /* Physical address */ ulong size; /* Maximum segment size in pages */ Page *(*pgalloc)(Segment*, ulong); /* Allocation if we need it */ void (*pgfree)(Page*); }; struct Sema { Rendez; long *addr; int waiting; Sema *next; Sema *prev; }; struct Segment { Ref; QLock lk; ushort steal; /* Page stealer lock */ ushort type; /* segment type */ ulong base; /* virtual base */ ulong top; /* virtual top */ ulong size; /* size in pages */ ulong fstart; /* start address in file for demand load */ ulong flen; /* length of segment in file */ int flushme; /* maintain icache for this segment */ Image *image; /* text in file attached to this segment */ Physseg *pseg; ulong* profile; /* Tick profile area */ Pte **map; int mapsize; Pte *ssegmap[SSEGMAPSIZE]; Lock semalock; Sema sema; ulong mark; /* portcountrefs */ }; enum { RENDLOG = 5, RENDHASH = 1<rendhash[(s)&((1<mnthash[(qid).path&((1< variadic */ }; /* * routines to access UART hardware */ struct PhysUart { char* name; Uart* (*pnp)(void); void (*enable)(Uart*, int); void (*disable)(Uart*); void (*kick)(Uart*); void (*dobreak)(Uart*, int); int (*baud)(Uart*, int); int (*bits)(Uart*, int); int (*stop)(Uart*, int); int (*parity)(Uart*, int); void (*modemctl)(Uart*, int); void (*rts)(Uart*, int); void (*dtr)(Uart*, int); long (*status)(Uart*, void*, long, long); void (*fifo)(Uart*, int); void (*power)(Uart*, int); int (*getc)(Uart*); /* polling versions, for iprint, rdb */ void (*putc)(Uart*, int); }; enum { Stagesize= 2048 }; /* * software UART */ struct Uart { void* regs; /* hardware stuff */ void* saveregs; /* place to put registers on power down */ char* name; /* internal name */ ulong freq; /* clock frequency */ int bits; /* bits per character */ int stop; /* stop bits */ int parity; /* even, odd or no parity */ int baud; /* baud rate */ PhysUart*phys; int console; /* used as a serial console */ int special; /* internal kernel device */ Uart* next; /* list of allocated uarts */ QLock; int type; /* ?? */ int dev; int opens; int enabled; Uart *elist; /* next enabled interface */ int perr; /* parity errors */ int ferr; /* framing errors */ int oerr; /* rcvr overruns */ int berr; /* no input buffers */ int serr; /* input queue overflow */ /* buffers */ int (*putc)(Queue*, int); Queue *iq; Queue *oq; Lock rlock; uchar istage[Stagesize]; uchar *iw; uchar *ir; uchar *ie; Lock tlock; /* transmit */ uchar ostage[Stagesize]; uchar *op; uchar *oe; int drain; int modem; /* hardware flow control on */ int xonoff; /* software flow control on */ int blocked; int cts, dsr, dcd; /* keep track of modem status */ int ctsbackoff; int hup_dsr, hup_dcd; /* send hangup upstream? */ int dohup; Rendez r; }; extern Uart* consuart; /* * performance timers, all units in perfticks */ struct Perf { ulong intrts; /* time of last interrupt */ ulong inintr; /* time since last clock tick in interrupt handlers */ ulong avg_inintr; /* avg time per clock tick in interrupt handlers */ ulong inidle; /* time since last clock tick in idle loop */ ulong avg_inidle; /* avg time per clock tick in idle loop */ ulong last; /* value of perfticks() at last clock tick */ ulong period; /* perfticks() per clock tick */ }; struct Watchdog { void (*enable)(void); /* watchdog enable */ void (*disable)(void); /* watchdog disable */ void (*restart)(void); /* watchdog restart */ void (*stat)(char*, char*); /* watchdog statistics */ }; /* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ enum { /* Queue.state */ Qstarve = (1<<0), /* consumer starved */ Qmsg = (1<<1), /* message stream */ Qclosed = (1<<2), /* queue has been closed/hungup */ Qflow = (1<<3), /* producer flow controlled */ Qcoalesce = (1<<4), /* coallesce packets on read */ Qkick = (1<<5), /* always call the kick routine after qwrite */ }; #define DEVDOTDOT -1 #pragma varargck type "I" uchar* #pragma varargck type "V" uchar* #pragma varargck type "E" uchar* #pragma varargck type "M" uchar* Timer { /* Public interface */ int tmode; /* See above */ vlong tns; /* meaning defined by mode */ void (*tf)9am/9port/portfns.h 664 0 0 25663 11334032360 12322ustar00nemosysvoid _assert(char*); void accounttime(void); Timer* addclock0link(void (*)(void), int); int addphysseg(Physseg*); void addbootfile(char*, uchar*, ulong); void addwatchdog(Watchdog*); Block* adjustblock(Block*, int); void alarmkproc(void*); Block* allocb(int); int anyhigher(void); int anyready(void); Image* attachimage(int, Chan*, ulong, ulong); Page* auxpage(void); Block* bl2mem(uchar*, Block*, int); int blocklen(Block*); void bootlinks(void); void cachedel(Image*, ulong); void cachepage(Page*, Image*); void callwithureg(void(*)(Ureg*)); char* chanpath(Chan*); void* realloc(void*, ulong); int canlock(Lock*); int canpage(Proc*); int canqlock(QLock*); int canrlock(RWlock*); void chandevinit(int); void chandevreset(int); void chandevshutdown(void); void chanfree(Chan*); void checkalarms(void); void checkb(Block*, char*); void cinit(void); Chan* cclone(Chan*); void cclose(Chan*); void ccloseq(Chan*); void closeegrp(Egrp*); void closefgrp(Fgrp*); void closepgrp(Pgrp*); void closergrp(Rgrp*); long clrfpintr(void); void cmderror(Cmdbuf*, char*); int cmount(Chan**, Chan*, int, char*); void confinit(void); int consactive(void); void (*consdebug)(void); void copen(Chan*); Block* concatblock(Block*); Block* copyblock(Block*, int); void copypage(Page*, Page*); void countpagerefs(ulong*, int); int cread(Chan*, uchar*, int, vlong); void cunmount(Chan*, Chan*); void cupdate(Chan*, uchar*, int, vlong); void cwrite(Chan*, uchar*, int, vlong); ulong dbgpc(Proc*); long decref(Ref*); int decrypt(void*, void*, int); void delay(int); Proc* dequeueproc(Schedq*, Proc*); Chan* devattach(int, char*); Block* devbread(Chan*, long, ulong); long devbwrite(Chan*, Block*, ulong); Chan* devclone(Chan*); int devconfig(int, char *, DevConf *); void devcreate(Chan*, char*, int, ulong); void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); Devgen devgen; void devinit(void); int devno(int, int); Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); void devpermcheck(char*, ulong, int); void devpower(int); void devremove(Chan*); void devreset(void); void devshutdown(void); int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); int devwstat(Chan*, uchar*, int); void drawactive(int); void drawcmap(void); void dumpaproc(Proc*); void dumpregs(Ureg*); void dumpstack(void); Fgrp* dupfgrp(Fgrp*); int duppage(Page*); void dupswap(Page*); void edfinit(Proc*); char* edfadmit(Proc*); int edfready(Proc*); void edfrecord(Proc*); void edfrun(Proc*, int); void edfstop(Proc*); void edfyield(void); int emptystr(char*); int encrypt(void*, void*, int); void envcpy(Egrp*, Egrp*); int eqchan(Chan*, Chan*, int); int eqchantdqid(Chan*, int, int, Qid, int); int eqqid(Qid, Qid); void error(char*); long execregs(ulong, ulong, ulong); void exhausted(char*); void exit(int); uvlong fastticks(uvlong*); uvlong fastticks2ns(uvlong); uvlong fastticks2us(uvlong); int fault(ulong, int); void fdclose(int, int); Chan* fdtochan(int, int, int, int); int findmount(Chan**, Mhead**, int, int, Qid); int fixfault(Segment*, ulong, int, int); void flushmmu(void); void forceclosefgrp(void); void forkchild(Proc*, Ureg*); void forkret(void); void free(void*); void freeb(Block*); void freeblist(Block*); int freebroken(void); void freepte(Segment*, Pte*); void getcolor(ulong, ulong*, ulong*, ulong*); ulong getmalloctag(void*); ulong getrealloctag(void*); void gotolabel(Label*); char* getconfenv(void); int haswaitq(void*); long hostdomainwrite(char*, int); long hostownerwrite(char*, int); void hzsched(void); Block* iallocb(int); void iallocsummary(void); long ibrk(ulong, int); void ilock(Lock*); void iunlock(Lock*); long incref(Ref*); void initseg(void); int iprint(char*, ...); void isdir(Chan*); int iseve(void); int islo(void); Segment* isoverlap(Proc*, ulong, int); int ispages(void*); int isphysseg(char*); void ixsummary(void); int kbdcr2nl(Queue*, int); int kbdgetmap(uint, int*, int*, Rune*); int kbdputc(Queue*, int); void kbdputmap(ushort, ushort, Rune); void kickpager(void); void killbig(char*); void kproc(char*, void(*)(void*), void*); void kprocchild(Proc*, void (*)(void*), void*); void (*kproftimer)(ulong); void ksetenv(char*, char*, int); void kstrcpy(char*, char*, int); void kstrdup(char**, char*); long latin1(Rune*, int); int lock(Lock*); void logopen(Log*); void logclose(Log*); char* logctl(Log*, int, char**, Logflag*); void logn(Log*, int, void*, int); long logread(Log*, void*, ulong, long); void log(Log*, int, char*, ...); Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); Page* lookpage(Image*, ulong); #define MS2NS(n) (((vlong)(n))*1000000LL) void machinit(void); void* mallocz(ulong, int); void* malloc(ulong); void* mallocalign(ulong, ulong, long, ulong); void mallocsummary(void); Block* mem2bl(uchar*, int); void mfreeseg(Segment*, ulong, int); void microdelay(int); uvlong mk64fract(uvlong, uvlong); void mkqid(Qid*, vlong, ulong, int); void mmurelease(Proc*); void mmuswitch(Proc*); Chan* mntauth(Chan*, char*); long mntversion(Chan*, char*, int, int); void mouseresize(void); void mountfree(Mount*); ulong ms2tk(ulong); ulong msize(void*); ulong ms2tk(ulong); uvlong ms2fastticks(ulong); void mul64fract(uvlong*, uvlong, uvlong); void muxclose(Mnt*); Chan* namec(char*, int, int, ulong); void nameerror(char*, char*); Chan* newchan(void); int newfd(Chan*); Mhead* newmhead(Chan*); Mount* newmount(Mhead*, Chan*, int, char*); Page* newpage(int, Segment **, ulong); Path* newpath(char*); Pgrp* newpgrp(void); Rgrp* newrgrp(void); Proc* newproc(void); void nexterror(void); int notify(Ureg*); int nrand(int); uvlong ns2fastticks(uvlong); int okaddr(ulong, ulong, int); int openmode(ulong); Block* packblock(Block*); Block* padblock(Block*, int); void pagechainhead(Page*); void pageinit(void); ulong pagenumber(Page*); void pagersummary(void); void panic(char*, ...); Cmdbuf* parsecmd(char *a, int n); void pathclose(Path*); ulong perfticks(void); void pexit(char*, int); void pgrpcpy(Pgrp*, Pgrp*); void pgrpnote(ulong, char*, long, int); void pio(Segment *, ulong, ulong, Page **); #define poperror() up->nerrlab-- void portcountpagerefs(ulong*, int); int postnote(Proc*, int, char*, int); int pprint(char*, ...); int preempted(void); void prflush(void); void printinit(void); ulong procalarm(ulong); void procctl(Proc*); void procdump(void); int procfdprint(Chan*, int, int, char*, int); int procindex(ulong); void procinit0(void); void procflushseg(Segment*); void procpriority(Proc*, int, int); Proc* proctab(int); extern void (*proctrace)(Proc*, int, vlong); void procwired(Proc*, int); Pte* ptealloc(void); Pte* ptecpy(Pte*); int pullblock(Block**, int); Block* pullupblock(Block*, int); Block* pullupqueue(Queue*, int); void putimage(Image*); void putmhead(Mhead*); void putmmu(ulong, ulong, Page*); void putpage(Page*); void putseg(Segment*); void putstrn(char*, int); void putswap(Page*); ulong pwait(Waitmsg*); void qaddlist(Queue*, Block*); Block* qbread(Queue*, int); long qbwrite(Queue*, Block*); Queue* qbypass(void (*)(void*, Block*), void*); int qcanread(Queue*); void qclose(Queue*); int qconsume(Queue*, void*, int); Block* qcopy(Queue*, int, ulong); int qdiscard(Queue*, int); void qflush(Queue*); void qfree(Queue*); int qfull(Queue*); Block* qget(Queue*); void qhangup(Queue*, char*); int qisclosed(Queue*); int qiwrite(Queue*, void*, int); int qlen(Queue*); void qlock(QLock*); Queue* qopen(int, int, void (*)(void*), void*); int qpass(Queue*, Block*); int qpassnolim(Queue*, Block*); int qproduce(Queue*, void*, int); void qputback(Queue*, Block*); long qread(Queue*, void*, int); Block* qremove(Queue*); void qreopen(Queue*); void qsetlimit(Queue*, int); void qunlock(QLock*); int qwindow(Queue*); int qwrite(Queue*, void*, int); void qnoblock(Queue*, int); int rand(void); void randominit(void); ulong randomread(void*, ulong); void rdb(void); int readnum(ulong, char*, ulong, ulong, int); int readstr(ulong, char*, ulong, char*); void ready(Proc*); void rebootcmd(int, char**); void reboot(void*, void*, ulong); void relocateseg(Segment*, ulong); void renameuser(char*, char*); void resched(char*); void resrcwait(char*); int return0(void*); void rlock(RWlock*); long rtctime(void); void runlock(RWlock*); Proc* runproc(void); void savefpregs(FPsave*); void sched(void); void scheddump(void); void schedinit(void); void (*screenputs)(char*, int); long seconds(void); ulong segattach(Proc*, ulong, char *, ulong, ulong); void segclock(ulong); void segpage(Segment*, Page*); int setcolor(ulong, ulong, ulong, ulong); void setkernur(Ureg*, Proc*); int setlabel(Label*); void setmalloctag(void*, ulong); void setrealloctag(void*, ulong); void setregisters(Ureg*, char*, char*, int); void setswapchan(Chan*); char* skipslash(char*); void sleep(Rendez*, int(*)(void*), void*); void* smalloc(ulong); int splhi(void); int spllo(void); void splx(int); void splxpc(int); char* srvname(Chan*); int swapcount(ulong); int swapfull(void); void swapinit(void); void timeradd(Timer*); void timerdel(Timer*); void timersinit(void); void timerintr(Ureg*, Tval); void timerset(Tval); ulong tk2ms(ulong); #define TK2MS(x) ((x)*(1000/HZ)) uvlong tod2fastticks(vlong); vlong todget(vlong*); void todsetfreq(vlong); void todinit(void); void todset(vlong, vlong, int); Block* trimblock(Block*, int, int); void tsleep(Rendez*, int (*)(void*), void*, ulong); int uartctl(Uart*, char*); int uartgetc(void); void uartkick(void*); void uartmouse(Uart*, int (*)(Queue*, int), int); void uartsetmouseputc(Uart*, int (*)(Queue*, int)); void uartputc(int); void uartputs(char*, int); void uartrecv(Uart*, char); int uartstageoutput(Uart*); void unbreak(Proc*); void uncachepage(Page*); long unionread(Chan*, void*, long); void unlock(Lock*); uvlong us2fastticks(uvlong); void userinit(void); ulong userpc(void); long userwrite(char*, int); void validaddr(ulong, ulong, int); void validname(char*, int); char* validnamedup(char*, int); void validstat(uchar*, int); void* vmemchr(void*, int, int); Proc* wakeup(Rendez*); int walk(Chan**, char**, int, int, int*); void wlock(RWlock*); void wunlock(RWlock*); void* xalloc(ulong); void* xallocz(ulong, int); void xfree(void*); void xhole(ulong, ulong); void xinit(void); int xmerge(void*, void*); void* xspanalloc(ulong, int, ulong); void xsummary(void); void yield(void); Segment* data2txt(Segment*); Segment* dupseg(Segment**, int, int); Segment* newseg(int, ulong, ulong); Segment* seg(Proc*, ulong, int); void hnputv(void*, uvlong); void hnputl(void*, uint); void hnputs(void*, ushort); uvlong nhgetv(void*); uint nhgetl(void*); ushort nhgets(void*); ulong µs(void); void _xinc(long*); long _xdec(long*); long lcycles(void); #pragma varargck argpos iprint 1 #pragma varargck argpos panic 1 #pragma varargck argpos pprint 1 d closeegrp(Egrp*); void closefgrp(Fgrp*); void closepgrp(Pgrp*); void cl9am/README 664 0 0 5300 11333023155 10224ustar00nemosys*** STILL WIP *** Apics are configured before running the interpreter. The interpreter seems to be working but requires more testing. It copies its config into the kernel; now we have to use it to configure devices, if we want to do so. NOTES: ----- Applying changes from 9pc/ and 9port/ to /sys/src/9/pc and port adds a devp9am.c driver and changes the boot sequence to start /boot/9am before boot or interrupts are set up. /boot/9am runs aml code from #9/acpitbl and writes configuration to #9. Then it asks #9 to initialize all devices (what main did) and execs boot. In the future the interpreter should be kept running as a user program to serve acpi drivers, if needed. GUIDE: ----- In pcf, add devp9am to devs and /386/9am to the boot dir. (and example is included in 9pc) In plan9.ini, add *noacpi=1 # to supress all of acpi from executing (!apics). *9am=1 # to ask for device configs, or =0 to disable *debug9am=-x # command line flags for /boot/9am See aml.pdf for an intro to the implementation (already out of date, but informative). TODO: ---- - more testing and start using conf info. Handle acpi devices like the power button. - add an interface for the kernel to ask the interpreter to execute methods. Perhaps make 9am read commands from #9/something and let it see which method must be called and which args to use. FILES: ----- See ./9CHANGES for a list of changes required in the kernel. ./9pc has /sys/src/9/pc files. ./9port has /sys/src/9/port files. The rest is the source for /boot/9am. aml.c - main program kernel.[ch] - compat. in case we move 9am into the kernel. this could go if 9am is kept at user level. acpi.[ch] - acpi code. [ this is the aml interpreter ] aml.h - definitions for the interpreter. amlconv.c - object casts and assignments. amlop.c - operation tables and implementation. amlrt.c - run time tools for the interpreter. USAGE: ----- When used as /boot/9am, args come from *debug9am, otherwise it uses argv. The interpreter evaluates aml supplied from #9/acpitbl or the file given to the `-f' option. Usual flags are -n don't do anything to the kernel -f file use file instead of #9/acpitbl (cp #9/acpitbl to a file to save for later) /path dump acpi ns under /path @/path dump acpi method code at path !/path call acpi method at path For example: cp /dev/acpitbl acpidump # preverse tables for testing 8.out # configure using acpi 8.out -x # idem, but report actions. 8.out / # idem, and dumpns / 8.out -nf acpidump / # dump ns resulting from acpidump 8.out @/SCMP # disassemble /SCMP You may combine multiple cmds in a single call: # configure all devices, tracing calls # to /_SB_/INI but do not trace /SCMP 8.out acpihalia +/_SB_/_INI -/SCMP %/ (void*), void*); void kprocchild(Proc*, void (*)(void*), void*); void (*kproftimer)(ulong); void ksetenv(char*, char*, int); void kstrcpy(char*, char*, int); void kstrdup(char**, char*); long latin1(Rune*, int); int lock(Lock*); void logopen(Log*); void logclose(Log*); char* logctl(Log*, int, char**, Logflag*9am/Test/ 775 0 0 0 11334032317 104065ustar00nemonemo9am/acme.dump 600 0 0 16001 11331376624 11160ustar00nemosys/usr/nemo /lib/font/bit/pelm/euro.8.font /lib/font/bit/lucm/unicode.9.font 0 33 66 f 0 1 4024 4034 1 1 48 9131 0 0 /sys/src/9/pc/apic.c Del Snarf | Look g timerset f 0 5 3004 3004 3 5 53 6698 0 0 /sys/src/9/pc/archacpi.c Del Snarf Undo | Look :/Pcmp f 0 8 6771 6771 91 8 45 6814 0 0 /sys/src/9/pc/mp.h Del Snarf Undo | Look io.h f 0 2 25738 25750 96 2 49 29665 0 0 /sys/src/9/pc/pci.c Del Snarf | Look pcimatchtbdf f 1 4 0 0 1 4 53 6835 0 0 /sys/src/cmd/9am/9am.c Del Snarf | Look Get apic.c:47 f 1 3 34470 34470 3 3 52 40375 0 0 /sys/src/9/pc/devp9am.c Del Snarf Undo | Look mkfile f 1 9 4499 4527 20 9 54 5134 0 0 /sys/src/9/pc/mp.c Del Snarf Undo | Look machno2apicno f 1 15 56 56 60 15 43 4179 0 0 /sys/src/9/pc/mkfile Del Snarf Undo | Look f 2 7 3548 3548 1 7 51 3949 0 0 /sys/src/9/pc/acpi.h Del Snarf Undo | Look Get wloc F 2 1 552 552 3 552 10 42 552 0 1 /sys/src/cmd/9am/+Errors Del Snarf | Look acpi.c:700: fmtprint(f, " %I", r->irq.flags); apic.c:115: dprint("\tintovr irq %d intr %d %I\n", apic.c:120: dprint("\tnmi intr %d %I\n", st->nmi.intr, apic.c:124: dprint("\tlnmi pid %d lint %d %I\n", apic.c:136: " iosv %d intr %d %I\n", apic.c:145: dprint("\tlx2nmi puid %d intr %d %I\n", 8c -FTVw acpi.c 8c -FTVw apic.c 8l -o 8.out 9am.8 amlrt.8 amlconv.8 amlop.8 acpi.8 apic.8 kernel.8 cp 8.out /386/bin/9am 8c -FTVw acpi.c 8c -FTVw apic.c 8l -o 8.out 9am.8 amlrt.8 amlconv.8 amlop.8 acpi.8 apic.8 kernel.8 cp 8.out /386/bin/9am f 2 12 4241 4268 4 12 39 6236 0 0 /sys/src/9/pc/i8253.c Del Snarf | Look f 2 13 1735 1735 6 13 52 16502 0 0 /sys/src/9/pc/archmp.c Del Snarf Undo | Look mk mp.8 F 2 4 4089 4089 97 4089 14 39 4089 0 1 /sys/src/9/pc/+Errors Del Snarf | Look 8c -FTVw archmp.c archmp.c:12 external redeclaration of: mpinit STATIC FUNC(VOID) VOID archmp.c:12 EXTERN FUNC(VOID) VOID mp.h:229 archmp.c:8 archmp.c:77 initialization of incompatible pointers: archmp IND FUNC(VOID) VOID and VOID warning: archmp.c:460 auto declared and not used: cp warning: archmp.c:459 auto declared and not used: ncpu archmp.c:546 function args not checked: mpstart archmp.c:666 name not declared: intin warning: archmp.c:550 used and not set: vno archmp.c:678 syntax error, last name: return mk: 8c -FTVw archmp.c : exit status=rc 241: 8c 243: error mk 199: error 8c -FTVw archmp.c archmp.c:77 initialization of incompatible pointers: archmp IND FUNC(VOID) VOID and VOID warning: archmp.c:460 auto declared and not used: cp warning: archmp.c:459 auto declared and not used: ncpu archmp.c:546 function args not checked: mpstart archmp.c:666 name not declared: intin warning: archmp.c:550 used and not set: vno archmp.c:678 syntax error, last name: return mk: 8c -FTVw archmp.c : exit status=rc 289: 8c 291: error mk 247: error 8c -FTVw archmp.c warning: archmp.c:460 auto declared and not used: cp warning: archmp.c:459 auto declared and not used: ncpu archmp.c:546 function args not checked: mpstart archmp.c:666 name not declared: intin warning: archmp.c:550 used and not set: vno archmp.c:678 syntax error, last name: return mk: 8c -FTVw archmp.c : exit status=rc 337: 8c 339: error mk 295: error 8c -FTVw archmp.c archmp.c:544 function args not checked: mpstart archmp.c:664 name not declared: intin warning: archmp.c:548 used and not set: vno archmp.c:676 syntax error, last name: return mk: 8c -FTVw archmp.c : exit status=rc 385: 8c 387: error mk 343: error 8c -FTVw archmp.c archmp.c:544 function args not checked: mpstart archmp.c:664 function args not checked: mprdthiw warning: archmp.c:548 used and not set: vno archmp.c:676 syntax error, last name: return mk: 8c -FTVw archmp.c : exit status=rc 433: 8c 435: error mk 391: error 8c -FTVw archmp.c archmp.c:544 function args not checked: mpstart archmp.c:664 function args not checked: mprdthiw 8c -FTVw archmp.c 8c -FTVw mp.c mp.c:211 name not declared: cp mp.c:212 name not declared: ncpu mp.c:212 name not declared: cp mp.c:213 name not declared: ncpu mp.c:214 name not declared: ncpu mp.c:217 name not declared: ncpu mp.c:219 name not declared: apic mp.c:219 name not declared: mpapic mp.c:219 name not declared: apic mp.c:219 name not declared: apic mp.c:219 name not declared: mpapic too many errors mk: 8c -FTVw mp.c : exit status=rc 577: 8c 579: error mk 535: error 8c -FTVw mp.c mp.c:221 name not declared: apic mp.c:221 name not declared: mpapic mp.c:221 name not declared: apic mp.c:221 name not declared: apic mp.c:221 name not declared: mpapic mp.c:224 name not declared: apic mp.c:225 name not declared: apic mp.c:226 name not declared: apic warning: mp.c:216 set and not used: ncpu warning: mp.c:219 set and not used: ncpu warning: mp.c:228 set and not used: ncpu mk: 8c -FTVw mp.c : exit status=rc 625: 8c 627: error mk 583: error 8c -FTVw mp.c mp.c:189 external redeclaration of: mpstart GLOBL FUNC(IND STRUCT Apic, IND STRUCT Apic, IND STRUCT Apic) VOID mp.c:189 GLOBL FUNC(IND STRUCT Apic) VOID mp.h:228 mp.c:9 mp.c:189 function inconsistently declared: mpstart warning: mp.c:222 set and not used: apic warning: mp.c:222 set and not used: apic mk: 8c -FTVw mp.c : exit status=rc 673: 8c 675: error mk 631: error 8c -FTVw mp.c mp.c:189 external redeclaration of: mpstart GLOBL FUNC(IND STRUCT Apic, IND STRUCT Apic, INT) VOID mp.c:189 GLOBL FUNC(IND STRUCT Apic, IND STRUCT Apic, IND STRUCT Apic) VOID mp.h:229 mp.c:9 mp.c:189 function inconsistently declared: mpstart mk: 8c -FTVw mp.c : exit status=rc 721: 8c 723: error mk 679: error 8c -FTVw mp.c 8c -FTVw archmp.c mk: 'archmp.8' is up to date 8c -FTVw mp.c mp.c:194 function args not checked: dprint mp.c:205 function args not checked: dprint mp.c:209 function args not checked: dprint mp.c:214 function args not checked: dprint warning: mp.c:220 unreachable code IF 8c -FTVw mp.c warning: mp.c:223 unreachable code IF f 2 6 824 824 98 6 51 26480 0 0 /sys/src/cmd/9am/acpi.c Del Snarf | Look mk install t of date, but informative). TODO: ---- - more testing and start using conf info. Handle acpi devices like the power button. - add an interface for the kernel to ask the interpreter to execute methods. Perhaps make 9am read commands from #9/something and let it see which method must be called and which args to use. FILES: ----- See ./9CHANGES for a list of changes required in the kernel. ./9pc has /sys/src/9/pc files. ./9port has /sys/src/9/port files. The rest is the source for /boot/9am. aml.c - mai9am/acpi.c 664 0 0 64044 11333076665 10474ustar00nemosys/* * ACPI 4.0 Support. * Still WIP. * */ #include "kernel.h" #include "acpi.h" #include "aml.h" static int acpidevconf(Aname *dn, int kernconf); static char* acpifname = "/dev/acpictl"; static char* regfname = "/dev/acpiregio"; static Atable* tfirst; /* loaded DSDT/SSDT/... tables */ static Atable* tlast; /* pointer to last table */ static int regfd = -1; static Aobj* lastreg; /* last region used for I/O */ static int lastaccsz; /* last access size used for I/O */ static Srat* srat; /* System resource affinity */ static Msct* msct; /* Maximum system characteristics table */ static Gpe gpes[64]; /* general purpose event handlers */ int acpifd = -1; int acpisz = 4; /* sizeof(acpiint) */ int acpiflags; /* for debug */ char *acpistackbottom; /* for debug */ void acpiapics(void) { Aobj *o, *args[Nmargs]; Aname *n; n = amlwalk("/PIC", Walkdev, acpins); if(n != nil && n->o != nil && n->o->type == Omethod){ args[0] = amlnewi(Papic); args[1] = nil; o = amlcall(n->o, args, nil, acpiflags); if(o == nil) xprint("call to _PIC failed\n"); amlclose(o); } } void acpictl(char *fmt, ...) { char *msg; va_list v; int l; va_start(v, fmt); msg = vsmprint(fmt, v); va_end(v); if(msg == nil) return; FLUSH; if((acpiflags&Edryrun) == 0){ l = strlen(msg); if(write(acpifd, msg, l) != l) xprint("#9/acpictl: '%s': %r\n", msg); }else dprint("ctl: %s\n", msg); free(msg); } void acpiglock(void) { /* get global lock */ } void acpigunlock(void) { /* clear global lock */ } static u64int l64get(u8int* p) { /* * Doing this as a define * #define l64get(p) (((u64int)l32get(p+4)<<32)|l32get(p)) * causes 8c to abort with "out of fixed registers". */ return (((u64int)l32get(p+4)<<32)|l32get(p)); } void acpigpes(void) { char name[40]; Aname *n; int i, some; some = 0; for(i = 0; i < nelem(gpes); i++){ seprint(name, name+sizeof(name), "/GPE/L%02X", i); n = amlwalk(name, 0, nil); if(n == nil || n->o == nil){ seprint(name, name+sizeof(name), "/GPE/E%02X", i); n = amlwalk(name, 0, nil); } if(n != nil && n->o != nil){ some++; incref(n->o); gpes[i].ho = n->o; acpictl("gpe %d %s", i, name); } } if(some == 0) dprint("no gpe handlers\n"); } static void acpipwr(Aobj *o, int i) { Aobj *v1, *v2; v1 = nth(o, 0); v2 = nth(o, 1); if(v1 != nil && v2 != nil && v1->type == Oint && v2->type == Oint) acpictl("pwr %d %#ullx %#ullx", i, v1->ival, v2->ival); else dprint("pwrs: not a _Sx value: %O\n", o); } void acpipwrs(void) { char name[40]; Aname *n; Aobj *o; int i, some; some = 0; for(i = 0; i <= 5; i++){ seprint(name, name+sizeof(name), "/_S%d_", i); n = amlwalk(name, 0, nil); if(n != nil && n->o != nil){ if(wasamlerror()) continue; o = amlcast(n->o, Opkg); popamlerror(); some++; acpipwr(o, i); amlclose(o); } } if(some == 0) dxprint("no power state values\n"); } /* * Perform I/O within region in access units of accsz bytes. * All units in bytes. */ long regio(Aobj *ro, void *p, uintptr off, long len, int accsz, int iswr) { long n; if(lastreg != ro || lastaccsz != accsz) acpictl("region %s %s %#llux %#ullx %d", ro->name->path, acpiregstr(ro->reg.spc), ro->reg.base, ro->reg.len, accsz); doprint("%s: %s %#p %#lx %#ulx\n", iswr ? "pwrite" : "pread", ro->name->path, p, len, off); if((acpiflags&Edryrun) == 0){ if(iswr) n = pwrite(regfd, p, len, off); else n = pread(regfd, p, len, off); if(n != len) dprint("regio: %r\n"); }else n = len; incref(ro); amlclose(lastreg); lastreg = ro; lastaccsz = accsz; return n; } static Atable* newtable(uchar *p) { Atable *t; Sdthdr *h; t = malloc(sizeof(Atable)); if(t == nil) panic("no memory for more aml tables"); t->tbl = p; h = (Sdthdr*)t->tbl; t->data = p + Sdthdrsz; t->is64 = h->rev >= 2; t->dlen = l32get(h->length) - Sdthdrsz; memmove(t->sig, h->sig, sizeof(h->sig)); t->sig[sizeof(t->sig)-1] = 0; memmove(t->oemid, h->oemid, sizeof(h->oemid)); t->oemtblid[sizeof(t->oemtblid)-1] = 0; memmove(t->oemtblid, h->oemtblid, sizeof(h->oemtblid)); t->oemtblid[sizeof(t->oemtblid)-1] = 0; t->next = nil; if(tfirst == nil) tfirst = tlast = t; else{ tlast->next = t; tlast = t; } return t; } static Res* psmallres(uchar *ps, int len) { Res *r; uint mask; int nb, n, rlen, type; if(len < 3) return nil; type = ps[0]>>3 & 0xF; if(0)dxprint("psmallres: type %#x\n", type); /* * some devices include many copies of these resources. */ if(type != ARirq && type != ARdma && type != ARio && type != ARfixio) return nil; r = amlmalloc(sizeof(Res)); switch(type){ case ARirq: r->type = Rirq; mask = l16get(ps+1); memset(r->irq.irqs, -1, sizeof(r->irq.irqs)); for(nb = n = 0; nb < 16 && mask != 0; nb++){ if((mask & 1) != 0) r->irq.irqs[n++] = nb; mask >>= 1; } if(n == 0){ free(r); return nil; } if(len < 4) r->irq.flags = IntEDGE|IntHIGH; else{ if((ps[3]&8) != 0) r->irq.flags = IntLOW; else r->irq.flags = IntHIGH; if((ps[3]&1) != 0) r->irq.flags = IntEDGE; else r->irq.flags = IntLEVEL; } return r; case ARdma: r->type = Rdma; r->dma.chans = ps[1]; r->dma.flags = ps[2]; break; case ARio: /* should be 8, but some have 4 bytes and a valid ioaddr */ if(len < 4) break; r->type = Rio; r->io.min = l16get(ps+2); r->io.max = r->io.min; r->io.align = 1; if(len >= 8){ r->io.max = l16get(ps+4); r->io.align = ps[6]; rlen = ps[7]; if(r->io.max - r->io.min + 1 != rlen) xprint("io: max %#llux min %#llux != len %ux\n", r->io.max, r->io.min, rlen); } return r; case ARfixio: if(len < 4) break; r->type = Rio; r->io.min = l16get(ps+1); rlen = ps[3]; r->io.max = r->io.min + (rlen - 1); r->io.align = 1; return r; default: panic("psmallres bug"); } xprint("psmallres: type %#x len %d failed\n", type, len); free(r); return nil; } static Res* pasres(Res *r, uchar *ps, int len) { uchar *pe; if(ps[0] == ARas16 && len < 16) goto Fail; if(ps[0] == ARas32 && len < 26) goto Fail; if(ps[0] == ARas64 && len < 46) goto Fail; if(ps[0] == ARxas && len != 56) goto Fail; r->type = ps[3] + Rmemas; if(r->type != Rmemas && r->type != Rioas && r->type != Rbusas) goto Fail; r->as.flags = (ps[4]&7) << 1; r->as.flags |= ps[5] << 8; /* type specific flags */ pe = ps + len; switch(ps[0]){ case ARas16: r->as.mask = l32get(ps+6); r->as.min = l32get(ps+8); r->as.max = l32get(ps+10); r->as.off = l32get(ps+12); r->as.len = l32get(ps+14); ps += 16; break; case ARas32: r->as.mask = l32get(ps+6); r->as.min = l32get(ps+10); r->as.max = l32get(ps+14); r->as.off = l32get(ps+18); r->as.len = l32get(ps+22); ps += 26; break; case ARas64: r->as.mask = l64get(ps+6); r->as.min = l64get(ps+14); r->as.max = l64get(ps+22); r->as.off = l64get(ps+30); r->as.len = l64get(ps+38); ps += 46; break; case ARxas: r->as.rev = ps[6]; r->as.mask = l64get(ps+8); r->as.min = l64get(ps+16); r->as.max = l64get(ps+24); r->as.off = l64get(ps+32); r->as.len = l64get(ps+40); r->as.attr = l64get(ps+48); return r; default: goto Fail; } if(ps[4] & 2) r->as.off *= -1; /* substr. decode */ if(pe-ps > 2){ r->as.idx = *ps++; r->as.src = amlmalloc(pe-ps+1); memmove(r->as.src, ps, pe-ps); r->as.src[pe-ps] = 0; } return r; Fail: xprint("pasres: type %#x len %d failed\n", ps[0], len); free(r); return nil; } static Res* plargeres(uchar *ps, int len) { Res *r; int n; int i; uchar *pe; u64int rlen; ps[0] &= 0x7F; if(0)dxprint("plargeres: type %#x\n", ps[0]); if(len < 3 || ps[0] == 0 || ps[0] == 3 || ps[0] == 4 || ps[0] > 0xB) return nil; r = amlmalloc(sizeof(Res)); switch(ps[0]){ case ARas16: case ARas32: case ARas64: case ARxas: return pasres(r, ps, len); case ARmem24: if(len < 12) goto Fail; r->type = Rmem; r->mem.isrw = ps[3] & 1; r->mem.min = l16get(ps+4) << 8; r->mem.max = l16get(ps+6) << 8; r->mem.align = l16get(ps+8); if(r->mem.align == 0) r->mem.align = 64 * 1024; rlen = l16get(ps+10) << 8; if(r->mem.max - r->mem.min != rlen - 1) xprint("mem max %llux min %llux != len %llux\n", r->mem.max, r->mem.min, rlen); break; case ARmem32: if(len < 20) goto Fail; r->type = Rmem; r->mem.isrw = ps[3] & 1; r->mem.min = l32get(ps+4); r->mem.max = l32get(ps+8); r->mem.align = l32get(ps+12); rlen = l32get(ps+16); if(r->mem.max - r->mem.min != rlen - 1) xprint("mem max %llux min %llux != len %llux\n", r->mem.max, r->mem.min, rlen); break; case ARfixmem32: if(len < 12) goto Fail; r->type = Rmem; r->mem.isrw = ps[3] & 1; r->mem.min = l32get(ps+4); rlen = l32get(ps+8); r->mem.max = r->mem.min + (rlen - 1); r->mem.align = 1; break; case ARxirq: pe = ps + len; if(len < 6) goto Fail; r->type = Rirq; if((ps[3] & 4) != 0) r->irq.flags |= IntLOW; else r->irq.flags |= IntHIGH; if((ps[3] & 2) != 0) r->irq.flags |= IntEDGE; else r->irq.flags |= IntLEVEL; /* if((ps[3] & 8) != 0) r->irq.flags |= Ishared; if((ps[3] & 1) != 0) r->irq.flags |= Iconsume; */ n = ps[4]; if(len < 5 + 4*n) goto Fail; ps += 5; for(i = 0; i < n && i < nelem(r->irq.irqs); i++){ r->irq.irqs[i] = l32get(ps); ps += 4; } if(pe-ps > 2){ r->irq.idx = *ps++; r->irq.src = amlmalloc(pe-ps+1); memmove(r->irq.src, ps, pe-ps); r->irq.src[pe-ps] = 0; } break; case ARreg: /* register field */ if(len != 15) goto Fail; r->type = Rfield; r->field.spc = ps[3]; r->field.len = ps[4]; r->field.off = ps[5]; if(ps[6] != 0) r->field.accsz = 1 << (ps[6] + 2); r->field.addr = l64get(ps+7); break; default: goto Fail; } return r; Fail: xprint("plargeres: type %#x len %d failed\n", ps[0], len); free(r); return nil; } /* * See if we have a dup resource. * We mallocz all resource, it's same to memcmp * but for the next pointer. */ static int isdup(Res *l, Res *r) { Res *saved; int rc; for(; l != nil; l = l->next){ if(l == r) continue; saved = l->next; l->next = nil; rc = memcmp(l, r, sizeof(Res)); l->next = saved; if(rc == 0) return 1; } return 0; } static Res* pres(Res **dr, uchar *ps, uchar *pe) { Res *r; Res **rl; int len; r = nil; for(rl = dr; *rl != nil; rl = &(*rl)->next) ; for(; ps < pe; ps += len){ /* len does not count itself; fix before calling */ if((ps[0] & 0x80) == 0){ len = 1 + (ps[0] & 3); if(len > pe - ps) len = pe - ps; *rl = psmallres(ps, len); }else if(pe - ps > 3){ len = 3 + l16get(ps+1); if(len > pe - ps) len = pe - ps; *rl = plargeres(ps, len); }else break; if(*rl != nil) if(isdup(*dr, *rl)){ free(*rl); *rl = nil; }else rl = &(*rl)->next; } return r; } char* acpiregstr(int id) { static char* dfl[] = { "mem", "io", "pcicfg", "embed", "smb", "cmos", "pcibar", }; static char buf[20]; /* BUG */ if(id >= 0 && id < nelem(dfl)) return dfl[id]; seprint(buf, buf+sizeof(buf), "spc:%#x", id); return buf; } static int Gfmt(Fmt* f) { static char* rnames[] = { "mem", "io", "pcicfg", "embed", "smb", "cmos", "pcibar", "ipmi"}; Gas *g; g = va_arg(f->args, Gas*); switch(g->spc){ case Rsysmem: case Rsysio: case Rembed: case Rsmbus: case Rcmos: case Rpcibar: case Ripmi: fmtprint(f, "[%s ", rnames[g->spc]); break; case Rpcicfg: fmtprint(f, "[pci "); fmtprint(f, "dev %#ulx ", (ulong)(g->addr >> 32) & 0xFFFF); fmtprint(f, "fn %#ulx ", (ulong)(g->addr & 0xFFFF0000) >> 16); fmtprint(f, "adr %#ulx ", (ulong)(g->addr &0xFFFF)); break; case Rfixedhw: fmtprint(f, "[hw "); break; default: fmtprint(f, "[spc=%#ux ", g->spc); } return fmtprint(f, "off %d len %d addr %#ullx sz %d]", g->off, g->len, g->addr, g->accsz); } /* * Apic interrupt flags */ static int Ifmt(Fmt *f) { int r; char flg[] = "--"; r = va_arg(f->args, int); if(r == -1) return fmtstrcpy(f, "none"); if((r&IntELMASK) == IntEDGE) flg[0] = 'e'; if((r&IntELMASK) == IntLEVEL) flg[0] = 'l'; if((r&IntPOMASK) == IntHIGH) flg[1] = 'h'; if((r&IntPOMASK) == IntLOW) flg[1] = 'l'; return fmtstrcpy(f, flg); } /* * print a resource in the format wanted by the kernel. */ static int Rfmt(Fmt *f) { Res *r; int i; char *nm[] = { "mem", "io", "bus"}; r = va_arg(f->args, Res*); if(r == nil) return fmtprint(f, ""); switch(r->type){ case Rirq: fmtprint(f, "irq %I", r->irq.flags); for(i = 0; i < nelem(r->irq.irqs); i++) if(r->irq.irqs[i] == ~0) break; else fmtprint(f, " %d", r->irq.irqs[i]); break; case Rdma: return fmtprint(f, "dma %#ux %#ux", r->dma.chans, r->dma.flags); case Rio: return fmtprint(f, "io %#08llux %#08llux %#ux", r->io.min, r->io.max, r->io.align); case Rmem: return fmtprint(f, "mem %s %#08llux %#08llux %#ux", r->mem.isrw ? "rw" : "ro", r->mem.min, r->mem.max, r->mem.align); case Rioas: case Rmemas: case Rbusas: fmtprint(f, "as %s %#ux", nm[r->type - Rmemas], r->as.flags); fmtprint(f, " %#ullx", r->as.mask); fmtprint(f, " %#08llux %#08llux", r->as.min, r->as.max); fmtprint(f, " %#ullx %#llx", r->as.len, r->as.off); fmtprint(f, " %#ullx", r->as.attr); break; case Rpir: fmtprint(f, "pir %2d %d",r->pir.dno, r->pir.pin); if(r->pir.src != nil && r->pir.src->type == Odev) fmtprint(f, " %ulld -1", r->pir.src->dev.uid); else fmtprint(f, " -1 %d", r->pir.srcint); break; case Rbbn: return fmtprint(f, "bbn %d", r->bbn); default: panic("Rfmt with bad resource type"); } return 0; } /* * Like Rfmt, but for our debug messages and not for * the kernel. */ static int Dfmt(Fmt *f) { Res *r; int i; char *nm[] = { "mem", "io", "bus"}; r = va_arg(f->args, Res*); if(r == nil) return fmtprint(f, ""); switch(r->type){ case Rirq: fmtstrcpy(f, "irqs:"); for(i = 0; i < nelem(r->irq.irqs); i++) if(r->irq.irqs[i] == ~0) break; else fmtprint(f, " %d", r->irq.irqs[i]); if(r->irq.src != nil) fmtprint(f, " %s[%d]", r->irq.src, r->irq.idx); fmtprint(f, " %I", r->irq.flags); break; case Rdma: return fmtprint(f, "dma chmask %#ux flags %#ux", r->dma.chans, r->dma.flags); case Rio: return fmtprint(f, "io [%#08llux-%#08llux] align %#ux", r->io.min, r->io.max, r->io.align); case Rmem: return fmtprint(f, "mem %s [%#08llux-%#08llux] align %#ux", r->mem.isrw ? "rw" : "ro", r->mem.min, r->mem.max, r->mem.align); case Rioas: case Rmemas: case Rbusas: fmtprint(f, "spc:%s %#ullx@", nm[r->type - Rmemas], r->as.len); if(r->as.flags&Asminfixed) fmtstrcpy(f, "["); else fmtstrcpy(f, "("); fmtprint(f, "%#08llux-%#08llux", r->as.min, r->as.max); if(r->as.flags&Asmaxfixed) fmtstrcpy(f, "]"); else fmtstrcpy(f, ")"); fmtprint(f, " %#+llx flg %#ux", r->as.off, r->as.flags); if(r->as.mask) fmtprint(f, " mask %#ullx", r->as.mask); if(r->as.attr&Muc) fmtprint(f, " uc"); if(r->as.attr&Mwc) fmtprint(f, " wc"); if(r->as.attr&Mwt) fmtprint(f, " wt"); if(r->as.attr&Mwb) fmtprint(f, " wb"); if(r->as.attr&Muce) fmtprint(f, " sem"); if(r->as.attr&Mnvr) fmtprint(f, " nvr"); if(r->as.src != nil) fmtprint(f, " %s[%d]", r->as.src, r->as.idx); break; case Rfield: return fmtprint(f, "field %G", &r->field); case Rpir: fmtprint(f, "pir dev %2d int%c",r->pir.dno, 'a'+r->pir.pin-1); if(r->pir.src != nil && r->pir.src->name != nil) fmtprint(f, " src %s crs[%d]", r->pir.src->name->path, r->pir.idx); if(r->pir.srcint >= 0) fmtprint(f, " srcint %d", r->pir.srcint); break; case Rbbn: return fmtprint(f, "bbn %d", r->bbn); default: return fmtprint(f, "", r->type); } return 0; } static void confaddr(Aobj *o, Aname *n) { Aobj *vo, *r, *v; r = nil; v = n->o; if(v->type == Omethod && v->method.nargs == 0) v = r = amlcall(v, nil, nil, acpiflags); vo = amlcast(v, Oint); o->dev.addr = vo->ival; amlclose(vo); amlclose(r); } static void confuid(Aobj *o, Aname *n) { Aobj *vo, *r, *v; r = nil; v = n->o; if(v->type == Omethod && v->method.nargs == 0) v = r = amlcall(v, nil, nil, acpiflags); vo = amlcast(v, Oint); o->dev.uid = vo->ival; amlclose(vo); amlclose(r); } static void confid(Aobj *o, Aname *n) { char id[8]; Aobj *r, *v; static char hex[] = "0123456789ABCDEF"; if(o->dev.pnpid != nil) return; r = nil; v = n->o; if(v->type == Omethod && v->method.nargs == 0) v = r = amlcall(v, nil, nil, acpiflags); switch(v->type){ case Ostr: case Odecstr: kstrdup(&o->dev.pnpid, v->sval); break; case Oint: /* curses taken from bsd code */ id[0] = '@' + (v->ival >> 2 & 0x1f); id[1] = '@' + (v->ival << 3 & 0x18) + (v->ival >> 13 & 0x7); id[2] = '@' + (v->ival >> 8 & 0x1f); id[3] = hex[v->ival >> 20 & 0xf]; id[4] = hex[v->ival >> 16 & 0xf]; id[5] = hex[v->ival >> 28 & 0xf]; id[6] = hex[v->ival >> 24 & 0xf]; id[7] = 0; kstrdup(&o->dev.pnpid,id); break; } if(r != nil) amlclose(r); } static void confname(Aobj *o, Aname *n) { Aobj *vo, *v; v = n->o; if(o->dev.name == nil){ vo = amlcast(v, Ostr); kstrdup(&o->dev.name,vo->sval); } } static void confres(Aobj *o, Aname *n) { Aobj *vo, *ro, *v; Res **rl; v = n->o; ro = nil; if(v->type == Omethod && v->method.nargs == 0) v = ro = amlcall(v, nil, nil, acpiflags); vo = amlcast(v, Obuf); if(vo != nil){ if(strcmp(n->s, "_PRS") == 0) rl = &o->dev.other; else rl = &o->dev.res; pres(rl, vo->buf.p, vo->buf.p + vo->buf.len); amlclose(vo); } amlclose(ro); } static void confbbn(Aobj *o, Aname *n) { Aobj *v, *ro, *vo; Res *r; v = n->o; ro = nil; if(v->type == Omethod && v->method.nargs == 0) v = ro = amlcall(v, nil, nil, acpiflags); vo = amlcast(v, Oint); if(vo != nil){ r = amlmalloc(sizeof(Res)); r->type = Rbbn; r->bbn = vo->ival; r->next = o->dev.res; o->dev.res = r; amlclose(vo); } amlclose(ro); } /* * We add 1 to the pin number so that pin #1 is inta, * matching the numbers used by link devices and we can * recognize links w/o uid (uid == 0). */ static void confpir(Aobj *o, Aname *n) { Aobj *ro, *v, *el, *e; Res *r, **rl; Aname *snam; v = n->o; ro = nil; if(v->type == Omethod && v->method.nargs == 0) v = ro = amlcall(v, nil, nil, acpiflags); if(v == nil || v->type != Opkg){ amlclose(ro); return; } for(rl = &o->dev.res; *rl != nil; rl = &(*rl)->next) ; for(el = v->list.hd; el != nil; el = el->next){ if(el->type != Opkg || el->list.n != 4) continue; r = *rl = amlmalloc(sizeof(Res)); rl = &r->next; r->type = Rpir; e = el->list.hd; if(e == nil || e->type != Oint) continue; /* e->ival is devnb, funnb */ r->pir.dno = e->ival>>16 & 0xFFFF; e = e->next; if(e == nil || e->type != Oint) continue; r->pir.pin = e->ival + 1; e = e->next; if(e == nil) continue; r->pir.srcint = -1; switch(e->type){ case Oint: r->pir.srcint = 0; break; case Ostr: case Oname: snam = amlwalk(e->sval, Walkns, n); if(snam == nil) xprint("confpir: %s not found\n", e->sval); else if(snam->o != nil && snam->o->type == Odev){ acpidevconf(snam, 0); incref(snam->o); /* this breaks stack checks */ r->pir.src = snam->o; } else xprint("confpir: %s is not a device\n", e->sval); break; default: continue; } e = e->next; if(e == nil || e->type != Oint) continue; if(r->pir.srcint == 0) r->pir.srcint = e->ival; else r->pir.idx = e->ival; } } static int devcall(Aobj *dobj, char *name, Aobj **rp, int type) { Aname *dn, *n; Aobj *o; dn = dobj->name; if(rp != nil) *rp = nil; n = amlwalk(name, Walkdev, dn); if(n == nil) return 0; if(n->o == nil || n->o->type != Omethod || n->o->method.nargs > 0){ xprint("acpi: %s: bad %s\n", dn->path, name); return -1; } o = amlcall(n->o, nil, nil, acpiflags); if(o == nil){ xprint("acpi: %s: %s: %s\n", dn->path, name, amlerrstk.msg); return -1; } if(type >= 0 && o->type != type){ xprint("acpi: %s: %s: not %s\n", dn->path, name, amltname(type)); amlclose(o); return -1; } if(rp != nil) *rp = o; else amlclose(o); return 0; } /* * XXX: Could be _MAT objects under processors or apcis, * that are buffers in MADT format. I've seen none so far. * * under procs: parse lapic, lsapic, lapicnmi structs. * under apic: parse ioapic, iosapic, intsrc and their overrides. * in both cases, ignore other entries in the table. */ static struct{ char *s; void (*f)(Aobj*, Aname*); }confs[] = { { "_ADR", confaddr }, { "_HID", confid }, { "_CID", confid }, { "_UID", confuid }, { "_STR", confname }, { "_DDN", confname }, { "_PRS", confres }, { "_CRS", confres }, { "_PRT", confpir }, { "_BBN", confbbn }, }; static void printres(Res *r, char *p) { for(; r != nil; r = r->next){ if(1)dprint("res: %s%D\n", p, r); switch(r->type){ case Rirq: case Rdma: case Rio: case Rmem: case Rmemas: case Rioas: case Rbusas: case Rpir: case Rbbn: acpictl("%s%R\n", p, r); break; } } } static int acpidevconf(Aname *dn, int kconf) { Aname *n; Aobj *dobj, *o; int rc, i; dobj = dn->o; if(strncmp(dn->s, "LNK", 3) == 0 && dn->s[3] >= 'A' && dn->s[3] <= 'Z') o = nil; /* _STA is meaningless for links */ else if(devcall(dobj, "_STA", &o, Oint) < 0){ xprint("%s: _STA failed\n", dn->path); return -1; } if(o != nil){ rc = o->ival; amlclose(o); }else rc = Staok|Stapresent|Staenabled; if((acpiflags&Edryrun) == 0 && (rc&Stapresent) == 0){ dprint("confdev: %s: not present\n", dn->path); return -1; } if(devcall(dobj, "_INI", &o, Oint) < 0){ xprint("%s: _INI failed\n", dn->path); return -1; } amlclose(o); o = nil; for(i = 0; i < nelem(confs); i++){ n = amlwalk(confs[i].s, Walkdev, dn); if(n != nil) confs[i].f(dn->o, n); } if(dobj->dev.name == nil) kstrdup(&dobj->dev.name, dn->s); /* get a name */ if(dobj->dev.pnpid == nil) kstrdup(&dn->o->dev.pnpid, "PNPnone"); if(kconf == 0) return 0; if((acpiflags&Edryrun) == 0 && ((rc&Staok) == 0 || (rc&Staenabled) == 0)){ dprint("device %s is not enabled\n", dn->path); return -1; } acpictl("dev %s %s %s %ulld %#llux\n", dn->path, dobj->dev.name, dobj->dev.pnpid, dobj->dev.uid, dobj->dev.addr); printres(dobj->dev.res, ""); printres(dobj->dev.other, "o"); return 0; } /* * initialize and configure all devices under n. * due to acpi bugs and to our own ones, this may be dangerous. */ void acpiconf(Aname *n) { Aobj *o; Aname *cn; int isdev; fprint(2, "acpiconf\n"); o = n->o; isdev = o != nil && o->type == Odev; if(isdev){ fprint(2, "acpiconf: dev %s\n", n->s); if(acpidevconf(n, 1) < 0) return; if(n->hasdevs) acpictl("[\n"); dprint("acpiconf: dev %s done\n", n->path); } for(cn = n->childhd; cn != nil; cn = cn->sibling) acpiconf(cn); if(isdev && n->hasdevs) acpictl("]\n"); } static void dumpsrat(Srat *st) { if((acpiflags&Edebugflags) == 0) return; dprint("acpi: srat:\n"); for(; st != nil; st = st->next) switch(st->type){ case SRlapic: dprint("\tlapic: dom %d apic %d sapic %d clk %d\n", st->lapic.dom, st->lapic.apic, st->lapic.sapic, st->lapic.clkdom); break; case SRmem: dprint("\tmem: dom %d %#ullx %#ullx %c%c\n", st->mem.dom, st->mem.addr, st->mem.len, st->mem.hplug?'h':'-', st->mem.nvram?'n':'-'); break; case SRlx2apic: dprint("\tlx2apic: dom %d apic %d clk %d\n", st->lx2apic.dom, st->lx2apic.apic, st->lx2apic.clkdom); break; default: dprint("\t\n"); } dprint("\n"); } int acpisrat(uchar *p, int len) { Srat **stl, *st; uchar *pe; int stlen, flags; if(srat != nil){ xprint("acpi: two SRATs?\n"); return -1; } stl = &srat; pe = p + len; for(p += 48; p < pe; p += stlen){ st = amlmalloc(sizeof(Srat)); st->type = p[0]; st->next = nil; stlen = p[1]; switch(st->type){ case SRlapic: st->lapic.dom = p[2] | p[9]<<24| p[10]<<16 | p[11]<<8; st->lapic.apic = p[3]; st->lapic.sapic = p[8]; st->lapic.clkdom = l32get(p+12); if(l32get(p+4) == 0){ free(st); st = nil; } break; case SRmem: st->mem.dom = l32get(p+2); st->mem.addr = l64get(p+8); st->mem.len = l64get(p+16); flags = l32get(p+28); if((flags&1) == 0){ /* not enabled */ free(st); st = nil; }else{ st->mem.hplug = flags & 2; st->mem.nvram = flags & 4; } break; case SRlx2apic: st->lx2apic.dom = l32get(p+4); st->lx2apic.apic = l32get(p+8); st->lx2apic.clkdom = l32get(p+16); if(l32get(p+12) == 0){ free(st); st = nil; } break; default: xprint("unknown SRAT structure\n"); free(st); st = nil; } if(st != nil){ *stl = st; stl = &st->next; } } dumpsrat(srat); return 0; } static void dumpmsct(Msct *msct) { Mdom *st; if((acpiflags&Edebugflags) == 0) return; dprint("acpi: msct: %d doms %d clkdoms %#ullx maxpa\n", msct->ndoms, msct->nclkdoms, msct->maxpa); for(st = msct->dom; st != nil; st = st->next) dprint("\t[%d:%d] %d maxproc %#ullx maxmmem\n", st->start, st->end, st->maxproc, st->maxmem); dprint("\n"); } int acpimsct(uchar *p, int len) { uchar *pe; Mdom **stl, *st; int off; msct = amlmalloc(sizeof(Msct)); msct->ndoms = l32get(p+40) + 1; msct->nclkdoms = l32get(p+44) + 1; msct->maxpa = l64get(p+48); msct->dom = nil; stl = &msct->dom; pe = p + len; off = l32get(p+36); for(p += off; p < pe; p += 22){ st = amlmalloc(sizeof(Mdom)); st->next = nil; st->start = l32get(p+2); st->end = l32get(p+6); st->maxproc = l32get(p+10); st->maxmem = l64get(p+14); *stl = st; stl = &st->next; } dumpmsct(msct); return 0; } /* * Both DSDT and SSDT */ int acpidsdt(uchar *p, int len) { Atable *t; char sig[5]; if(len < Sdthdrsz) return -1; memmove(sig, p, 4); sig[4] = 0; if(strcmp(sig, "DSDT") != 0 && strcmp(sig, "SSDT") != 0) return -1; t = newtable(p); if(wasamlerror()) return -1; /* * Beware that this may be due to a load operation, * which means that evaluation would be nested in * whichever current environment may be found at * amlenv. */ amleval(t->data, t->data+t->dlen, nil, nil, t, acpiflags); popamlerror(); return 1; /* keep the aml code allocated */ } int acpiinit(void) { fmtinstall('D', Dfmt); fmtinstall('R', Rfmt); fmtinstall('I', Ifmt); fmtinstall('G', Gfmt); if((acpiflags&Edryrun) == 0){ if(acpifd < 0) acpifd = open(acpifname, ORDWR); if(regfd < 0) regfd = open(regfname, ORDWR); if(regfd < 0 || acpifd < 0){ xprint("acpi: can't open acpi device: %r\n"); return -1; } } return 0; } 9am/acpi.h 664 0 0 21340 11332626266 10466ustar00nemosys/* * ACPI 4.0 constants and definitions. */ typedef struct Daddr Daddr; typedef struct Atable Atable; typedef struct Facs Facs; typedef struct Fadt Fadt; typedef struct Gas Gas; typedef struct Gpe Gpe; typedef struct Res Res; typedef struct Rsdp Rsdp; typedef struct Sdthdr Sdthdr; typedef struct Xsdt Xsdt; typedef struct Aname Aname; typedef struct Aobj Aobj; typedef struct Msct Msct; typedef struct Srat Srat; typedef struct Mdom Mdom; enum { Sdthdrsz = 36, /* size of SDT header */ /* ACPI regions. Gas ids */ Rsysmem = 0, Rsysio, Rpcicfg, Rembed, Rsmbus, Rcmos, Rpcibar, Ripmi, Rfixedhw = 0x7f, /* Global lock bits */ GLpending = 0, GLowned = 1, /* resource types */ Rirq = 0, Rdma, Rio, Rmem, Rmemas, /* mem address space */ Rioas, /* io address space */ Rbusas, /* bus address space */ Rfield, /* register field */ /* fake ones, sw */ Rpir, /* pci interrupt routing, from _PRT */ Rbbn, /* base bus number */ /* ACPI small resource names */ ARirq = 4, ARdma = 5, ARio = 8, ARfixio = 9, /* ACPI large resource names (0, 4, and >11 are reserved) */ ARmem24 = 1, ARreg = 2, /* register, not region */ ARmem32 = 5, ARfixmem32 = 6, ARas32 = 7, ARas16 = 8, ARxirq = 9, ARas64 = 10, ARxas = 11, ARendtag = 0x78, /* end resource tag */ ARdontcksum = 0, /* don't checksum the resource */ /* SRAT types */ SRlapic = 0, /* Local apic/sapic affinity */ SRmem, /* Memory affinity */ SRlx2apic, /* x2 apic affinity */ /* Arg for _PIC */ Ppic = 0, /* PIC interrupt model */ Papic, /* APIC interrupt model */ Psapic, /* SAPIC interrupt model */ /* MPS INTI flags; used in Apic flags. * Resources, which do not use the same flags in ACPI, are * recorded also using MPS INT flags, for homegeneity * Flags IntNMI and IntMASKED are fake, used to report that * an interrupt should be NMI or masked. */ IntPOMASK = 0x03, /* polarity conforms to spec of bus */ IntHIGH = 0x01, /* active high */ IntLOW = 0x03, /* active low */ IntELMASK = 0x0C, /* trigger mode of APIC input signals */ IntEDGE = 0x04, /* edge-triggered */ IntLEVEL = 0x0C, /* level-triggered */ /* ACPI dma resource flags */ Dmachanmask = 3<<5, Dmachancompat = 0<<5, Dmachana = 1<<5, Dmachanb = 2<<5, Dmachanf = 3<<5, Dmabm = 1<<1, Dmaszmask = 3, Dma8 = 0, Dma8and16 = 1, Dma16 = 2, /* address space resource flags */ /* type specific flags are kept in the lo byte */ Asmaxfixed = 1<<(3+8), Asminfixed = 1<<(2+8), Asconsume = 1<<(0+8), /* consumes or produces this as? */ /* ACPI ext. as attrs */ Muc = 1, Mwc = 2, Mwt = 4, Mwb = 8, Muce = 0x10, /* uncached exported, semaphores */ Mnvr = 0x8000, /* PM1 control */ Pm1SciEn = 0x1, /* Generate SCI and not SMI */ /* _STA result value flags */ Stapresent = 0x1, /* device is present */ Staenabled = 0x2, /* device is enabled */ Staok = 0x8, /* device is ok */ }; /* Generic address structure. */ struct Gas { u8int spc; /* address space id */ u8int len; /* register size in bits */ u8int off; /* bit offset */ u8int accsz; /* 1: byte; 2: word; 3: dword; 4: qword */ u64int addr; /* address (or acpi encoded tbdf + reg) */ }; /* Root system description table pointer. * Used to locate the root system description table RSDT * (or the extended system description table from version 2) XSDT. * The XDST contains (after the DST header) a list of pointers to tables: * - FADT fixed acpi description table. * It points to the DSDT, AML code making the acpi namespace. * - SSDTs tables with AML code to add to the acpi namespace. * - pointers to other tables for apics, etc. */ struct Rsdp { u8int signature[8]; /* "RSD PTR " */ u8int rchecksum; u8int oemid[6]; u8int revision; u8int raddr[4]; /* RSDT */ u8int length[4]; u8int xaddr[8]; /* XSDT */ u8int xchecksum; /* XSDT */ u8int _33_[3]; /* reserved */ }; /* Header for ACPI description tables */ struct Sdthdr { u8int sig[4]; /* "FACP" or whatever */ u8int length[4]; u8int rev; u8int csum; u8int oemid[6]; u8int oemtblid[8]; u8int oemrev[4]; u8int creatorid[4]; u8int creatorrev[4]; }; /* Firmware control structure */ struct Facs { u32int hwsig; u32int wakingv; u32int glock; u32int flags; u64int xwakingv; u8int vers; u32int ospmflags; }; /* * software. */ /* System resource affinity table */ struct Srat { int type; Srat* next; union{ struct{ int dom; /* proximity domain */ int apic; /* apic id */ int sapic; /* sapic id */ int clkdom; /* clock domain */ } lapic; struct{ int dom; /* proximity domain */ u64int addr; /* base address */ u64int len; int hplug; /* hot pluggable */ int nvram; /* non volatile */ } mem; struct{ int dom; /* proximity domain */ int apic; /* x2 apic id */ int clkdom; /* clock domain */ } lx2apic; }; }; /* Maximum System Characteristics table */ struct Msct { int ndoms; /* number of domains */ int nclkdoms; /* number of clock domains */ u64int maxpa; /* max physical address */ Mdom* dom; /* domain information list */ }; struct Mdom { Mdom* next; int start; /* start dom id */ int end; /* end dom id */ int maxproc; /* max processor capacity */ u64int maxmem; /* max memory capacity */ }; /* XSDT/RSDT. 4/8 byte addresses starting at p. */ struct Xsdt { int len; int asize; u8int* p; }; /* Fixed ACPI description table. * Describes implementation and hardware registers. * PM* blocks are low level functions. * GPE* blocks refer to general purpose events. * P_* blocks are for processor features. * Has address for the DSDT. */ struct Fadt { u32int facs; u32int dsdt; /* 1 reserved */ u8int pmprofile; u16int sciint; u32int smicmd; u8int acpienable; u8int acpidisable; u8int s4biosreq; u8int pstatecnt; u32int pm1aevtblk; u32int pm1bevtblk; u32int pm1acntblk; u32int pm1bcntblk; u32int pm2cntblk; u32int pmtmrblk; u32int gpe0blk; u32int gpe1blk; u8int pm1evtlen; u8int pm1cntlen; u8int pm2cntlen; u8int pmtmrlen; u8int gpe0blklen; u8int gpe1blklen; u8int gp1base; u8int cstcnt; u16int plvl2lat; u16int plvl3lat; u16int flushsz; u16int flushstride; u8int dutyoff; u8int dutywidth; u8int dayalrm; u8int monalrm; u8int century; u16int iapcbootarch; /* 1 reserved */ u32int flags; Gas resetreg; u8int resetval; /* 3 reserved */ u64int xfacs; u64int xdsdt; Gas xpm1aevtblk; Gas xpm1bevtblk; Gas xpm1acntblk; Gas xpm1bcntblk; Gas xpm2cntblk; Gas xpmtmrblk; Gas xgpe0blk; Gas xgpe1blk; }; /* * ACPI table */ struct Atable { Atable* next; /* next table in list */ int is64; /* uses 64bits */ char sig[5]; /* signature */ char oemid[7]; /* oem id str. */ char oemtblid[9]; /* oem tbl. id str. */ uchar* tbl; /* pointer to table in memory */ uchar* data; /* pointer to aml bytecodes or nil */ long dlen; /* size of data in table, after Stdhdr */ }; /* * Our representation of resource settings. See 6.4 of acpi 4.0. */ struct Res { Res* next; /* in resource list */ int type; /* resource type */ union{ struct{ u32int irqs[16]; uint flags; int idx; /* index in src */ char* src; /* where resources come from */ }irq; struct{ uint chans; uint flags; }dma; struct{ u64int min; /* address of the range */ u64int max; /* address of the range */ uint align; int isrw; /* memory only, ro or rw? */ }io, mem; struct{ u64int mask; /* which bits are decoded */ u64int min; /* address on the other side */ u64int max; u64int len; /* can be < max-min+1 if not fixed */ vlong off; /* translation adds this */ int idx; /* index in src */ char* src; /* where resources come from */ int flags; int rev; /* acpi revision id or 0 */ u64int attr; }as; Gas field; /* non ACPI resources; invented by us to keep them here */ struct{ int dno; /* pci device number. */ int pin; /* 1 == INTA */ int srcint; /* global intr. nb. or -1 */ Aobj* src; /* source for interrupt */ int idx; }pir; /* pci intr. routing, from _PRT */ int bbn; /* base bus number */ }; }; struct Gpe { int nb; /* event number */ Aobj* ho; /* handler object */ }; #pragma varargck type "G" Gas* #pragma varargck type "I" int #pragma varargck type "I" uint #pragma varargck type "R" Res* #pragma varargck type "D" Res* /* |c/f2p acpi.c | c/psort */ void acpiapics(void); void acpiconf(Aname *n); void acpictl(char *fmt, ...); int acpidsdt(uchar *p, int len); void acpiglock(void); void acpigpes(void); void acpigunlock(void); int acpiinit(void); int acpimsct(uchar *p, int len); void acpipwrs(void); char* acpiregstr(int id); int acpisrat(uchar *p, int len); long regio(Aobj *ro, void *p, uintptr off, long len, int accsz, int iswr); extern int acpifd; extern int acpisz; /* number of bytes in integers */ extern int acpiflags; /* debug flags */ extern char *acpistackbottom; /* debug */ fmtinstall('R', Rfmt); fmtinstall('I', Ifmt); fmtinstall('G', Gfmt); if((acpiflags&Edryrun) == 0){ if(acpifd < 0) acpifd = open(acpifname, ORDWR); if(regfd < 0) regfd = open(regfname, ORDWR); if(regfd < 0 || acpifd < 0){ xprint("acpi: can't open acpi device: %r\n");9am/aml.h 644 0 0 31326 11331155073 10316ustar00nemosys/* * ACPI 4.0 constants and definitions used * for the AML interpreter, besides those * defined in acpi.h */ typedef struct Env Env; typedef struct Error Error; typedef struct Mframe Mframe; typedef struct Aname Aname; typedef struct Nbuf Nbuf; typedef struct Ndev Ndev; typedef struct Nevent Nevent; typedef struct Nfield Nfield; typedef struct Nlist Nlist; typedef struct Nmethod Nmethod; typedef struct Nmutex Nmutex; typedef struct Nproc Nproc; typedef struct Npwr Npwr; typedef struct Nref Nref; typedef struct Nreg Nreg; typedef struct Nscope Nscope; typedef struct Nslice Nslice; typedef struct Nstr Nstr; typedef struct Nthermal Nthermal; typedef struct Aobj Aobj; typedef struct Aobjhdr Aobjhdr; typedef struct Op Op; typedef struct Xop Xop; enum { /* AML Operation codes */ OpZero = 0x00, OpOne = 0x01, OpAlias = 0x06, OpName = 0x08, OpByteprefix = 0x0A, OpWordprefix = 0x0B, OpDwordprefix = 0x0C, OpStrprefix = 0x0D, OpQwordprefix = 0x0E, OpScope = 0x10, OpBuffer = 0x11, OpPackage = 0x12, OpVarpackage = 0x13, OpMethod = 0x14, OpDnameprefix = 0x2E, OpMnameprefix = 0x2F, OpExtprefix = 0x5B, OpMutex = 0x01, OpEvent = 0x02, OpCondrefof = 0x12, OpMkfield = 0x13, OpLoadtable = 0x1F, OpLoad = 0x20, OpStall = 0x21, OpSleep = 0x22, OpAcquire = 0x23, OpSignal = 0x24, OpWait = 0x25, OpReset = 0x26, OpRelease = 0x27, OpFrombcd = 0x28, OpTobcd = 0x29, OpUnload = 0x2A, OpRevision = 0x30, OpDebug = 0x31, OpFatal = 0x32, OpTimer = 0x33, OpOpregion = 0x80, OpField = 0x81, OpDevice = 0x82, OpProcessor = 0x83, OpPwrrsrc = 0x84, OpThermalzone = 0x85, OpIndexfield = 0x86, OpBankfield = 0x87, OpDataregion = 0x88, OpRootchar = 0x5C, OpParentprefix = 0x5E, OpNamechar = 0x5F, OpLocal0 = 0x60, OpLocal1 = 0x61, OpLocal2 = 0x62, OpLocal3 = 0x63, OpLocal4 = 0x64, OpLocal5 = 0x65, OpLocal6 = 0x66, OpLocal7 = 0x67, OpArg0 = 0x68, OpArg1 = 0x69, OpArg2 = 0x6A, OpArg3 = 0x6B, OpArg4 = 0x6C, OpArg5 = 0x6D, OpArg6 = 0x6E, OpStore = 0x70, OpRefof = 0x71, OpAdd = 0x72, OpConcat = 0x73, OpSub = 0x74, OpInc = 0x75, OpDec = 0x76, OpMultiply = 0x77, OpDivide = 0x78, OpShl = 0x79, OpShr = 0x7A, OpAnd = 0x7B, OpNand = 0x7C, OpOr = 0x7D, OpNor = 0x7E, OpXor = 0x7F, OpNot = 0x80, OpLeftsetbit = 0x81, OpRightsetbit = 0x82, OpDerefof = 0x83, OpConcatres = 0x84, OpMod = 0x85, OpNotify = 0x86, OpSizeof = 0x87, OpIndex = 0x88, OpMatch = 0x89, OpMkdwordfield = 0x8A, OpMkwordfield = 0x8B, OpMkbytefield = 0x8C, OpMkbitfield = 0x8D, OpObjecttype = 0x8E, OpMkqwordfield = 0x8F, OpLand = 0x90, OpLor = 0x91, OpLnot = 0x92, OpLequal = 0x93, OpLgreater = 0x94, OpLless = 0x95, OpTobuf = 0x96, OpTodecstr = 0x97, OpTohexstr = 0x98, OpToint = 0x99, OpTostr = 0x9C, OpCopyobject = 0x9D, OpMid = 0x9E, OpContinue = 0x9F, OpIf = 0xA0, OpElse = 0xA1, OpWhile = 0xA2, OpNop = 0xA3, OpReturn = 0xA4, OpBreak = 0xA5, OpBreakpoint = 0xCC, OpOnes = 0xFF, /* OpMatch ops */ Mtrue = 0, Meq, Mle, Mlt, Mge, Mgt, /* Field access types */ AFany = 0, AFbyte, AFword, AFdword, AFqword, AFbuffer, /* Field update type */ AFpreserve = 0, AFwrones, AFwrzeros, /* Aobject types returned by AobjectType() */ Tnone = 0, Tint, Tstr, Tbuf, Tpkg, Tfield, Tdev, Tevent, Tmethod, Tmutex, Treg, Tpwr, Tproc, Tthermal, Tslice, Thandle, Tdbg, /* Aobject types used by the interpreter. * Keep this order. amltname() depends on it. * Some of them are not real object types and are * used only for AML argument strings, as parsed by pargs. * Pargs also understands these argument chars: * - 'l' means packagelen * - 'b' means byte, 'w' word, 'd' d-word, * - 'f' fieldlist * - 's' name string * - 'n' name or arg or local * - 't' target (name, perhaps created, dst object, nil) * - 'o' any object, maybe a name. * Upper case args are evaluated by pargs to obtain the typed arg. * Other args are not evalutated before calling the operation. */ Onone = 0, Ofree, /* debug */ Oslice, /* buffer field or slice */ Oscope, /* scope */ Omethod, /* method */ Odev, /* device */ Oproc, /* processor */ Opwr, /* pwrrsrc */ Othermal, /* thermal zone */ Oref, /* ref to object */ Olocal, /* ref to local object */ Oarg, /* ref to arg object */ Ohandle, /* definition block handle */ Obuf = 'B', /* buf */ Oevent = 'E', /* sleep/wakeup */ Ofield = 'F', /* register field */ Oint = 'I', /* integer */ Oname = 'N', /* a name */ Omutex = 'M', /* mutex */ Opkg = 'P', /* package object (array) */ Oreg = 'R', /* data region */ Ostr = 'S', /* string */ Odecstr = 'D', /* like string, but bytes from buffer */ /* values are printed in decimal */ Oany = 'O', /* any object after resolving names */ Nmargs = 7, /* max nb. of method arguments */ Nmlcls = 8, /* max nb. of method locals. /* Env flags */ Ewastrue = 0x0001, /* last if condition was true */ Enoeval = 0x0002, /* parse but do not eval methods */ Enocall = 0x0008, /* names are names and not calls */ Eidump = 0x0010, /* dump instructions */ Exdump = 0x0020, /* dump actions */ Emdump = 0x0040, /* dump method instructions as well */ Eedump = 0x0080, /* dump environment after ops. */ Ebdump = 0x0100, /* dump ns changes */ Evdump = 0x0200, /* dump general debug diagnostics */ Eodump = 0x0400, /* report object i/o */ Escheck = 0x1000, /* check the stack (slows down a lot) */ Eabort = 0x2000, /* abort on errors */ Edryrun = 0x4000, /* do not perform I/O to /dev */ Edebugflags = 0x3FF0, /* is "debug" on in general? */ /* arbitrary constants */ Nargs = 10, /* max nb. AML op arguments */ Nerrors = 32, /* error stack depth */ /* amlcpy flag */ Nocast = 0, Cast, /* pamlblock flag */ Dontkeep = 0, Keep, /* amlwalk flag */ Walkns = 0, /* acpi search rules */ Walkcreating, /* idem, but create if not found */ Walkdev, /* search relative name in device */ }; struct Aname { char s[5]; /* NNNN\0 */ char* path; Aobj* o; int hasdevs; Aname* parent; Aname* childhd; /* first child */ Aname* childtl; /* last child */ Aname* sibling; /* pointer to next sibling */ Atable* table; /* [ds]sdt table id; for unload */ Aname* mnext; /* in list of per-method binds */ }; struct Nstr { char *aml; char *str; }; struct Error { Label label[Nerrors]; int nerr; char msg[128]; }; typedef void (*Opx)(void); struct Nbuf { uchar* p; /* data */ ulong len; /* number of bytes */ }; struct Nlist { Aobj* hd; /* first object */ int n; /* number of objects in packages */ }; /* * bit field for a region, or banked-register bit field, or * (index,data) register bit field. */ struct Nfield { char* fname; /* field name */ int accsz; /* access sz in bits for source*/ int locking; /* needs locking */ int update; /* how to update */ uintptr off; /* in bits */ int len; /* in bits */ Aobj* reg; /* region the field is for */ Aobj* bank; /* bank name (for banked field) or nil */ Aobj* bankval; /* bank value (for banked field) */ Aobj* idx; /* index reg. (for index field) or nil */ Aobj* idxval; /* value for index */ Aobj* data; /* data (for index field) or nil */ }; /* * buffer field. slice of bits in buffer. */ struct Nslice { Aobj* src; /* source Obuf object */ uintptr off; /* starting bit index */ uintptr len; /* number of bits */ }; struct Nreg { int spc; /* io space */ u64int base; /* address, physical */ uchar* p; /* address, kmapped */ u64int len; }; struct Nscope { char* name; uchar* pe; /* end of AML for it */ }; /* * ACPI device */ struct Ndev { char* name; char* pnpid; /* pnp id */ u64int uid; /* unique id valid within same pnpid */ u64int addr; /* address */ Res* res; /* current resource settings */ Res* other; /* other possible res. settings */ uchar* pe; void (*handler)(Aobj*, int); /* for events */ void* aux; }; struct Nmethod { char* name; int nargs; /* arity */ int isexcl; /* is serialized? */ int synclvl; int trace; /* 1: on; -1: off; 0: as you were */ uchar* ps; /* aml for this method */ uchar* pe; /* end of aml */ void (*f)(Aobj*); /* C implementation for builtins */ }; struct Nthermal { char* name; void (*handler)(Aobj*, int); /* for events */ void* aux; }; struct Npwr { char* name; int syslvl; int rorder; }; struct Nproc { char* name; int id; uintptr baddr; int blen; void (*handler)(Aobj*, int); /* for events */ void* aux; }; struct Nmutex { int set; int count; int flags; /* sync level */ }; struct Nevent { int fire; }; struct Nref { Aobj* o; /* reference to object */ int no; /* number, for debug */ }; struct Aobjhdr { Ref; int type; /* for this object */ Aname* name; /* ACPI name if bound or nil */ Aobj* next; /* in list */ }; /* * ACPI Aobject. */ struct Aobj { Aobjhdr; union{ u64int ival; /* doubles as handle */ char* sval; /* strings and names */ Nbuf buf; /* byte buffer */ Nslice slice; /* buffer slice (bits) */ Nreg reg; /* memory, i/o, ... region */ Nfield field; /* slice (bits) of object */ Nref oref; /* local, ref, arg */ Nlist list; /* packages */ Nscope scope; /* for parsing */ Nmethod method; Ndev dev; Npwr pwr; Nproc proc; Nthermal thermal; Nmutex mutex; Nevent event; }; }; /* * AML operation */ struct Op { Opx x; /* implementation */ char* name; /* debug */ int flags; /* call on dry runs? Print { } on dumps? */ char* args; /* arguments, for AML parsing */ int ncalls; /* how many times used */ }; /* * To locate extended operations */ struct Xop { int ic; /* instruction code */ Op op; }; /* * Method activation frame. */ struct Mframe { Aobj* args[Nmargs]; /* method arguments */ int nargs; Aobj* lcls[Nmlcls]; /* method locals */ int nlcls; Aobj** targs; /* tmp. method args while parsing them */ Aname* binds; /* done by this method call */ }; /* * Evaluation environment supplied to all * AML operations. * This is used both to narrow the program * during parsing and to support evaluation scopes. * Envs are nested using the C stack during * AML parsing and evaluation. */ struct Env { char* name; /* debug */ Env* prev; /* in env stack */ int ic; /* instruction code */ uchar* ps; /* start of what's left of program (PC) */ uchar* pe; /* end of program */ int lvl; /* env nesting level (debug) */ Op* op; /* current op */ uchar* opp0; /* operation address 0 */ Nlist olist; /* list of obj in env */ Aobj* args[Nargs]; /* arguments for op */ int nargs; /* max number of args used */ uchar* argpe; /* pe resulting from arg 'l' */ Aobj* res; /* result from op */ Mframe* mp; /* Method frame pointer */ Aname* dot; /* current acpi name */ Atable* table; /* [ds]sdt table id */ int flags; }; /* print objects, tabs to indent, prog counters */ #pragma varargck type "O" Aobj* #pragma varargck type ">" int #pragma varargck type "!" void #define FmtSharp 8 #define wasamlerror() setlabel(&amlerrstk.label[amlerrstk.nerr++]) #define l16get(p) (((p)[1]<<8)|(p)[0]) #define l32get(p) (((u32int)l16get(p+2)<<16)|l16get(p)) /* |c/f2p amlconv.c amlop.c amlrt.c | c/psort */ void amlbind(char *s, Aobj *o); Aobj* amlcall(Aobj *mo, Aobj **args, Atable *table, int flags); Aobj* amlcast(Aobj *o, int twanted); void amlclean(Aobj *o); void amlclose(Aobj *o); void amlcpy(Aobj *o, Aobj *so, int castok); void amlenvdump(Env *e); void amlerror(char* msg, ...); char* amleval(uchar *ps, uchar *pe, Aobj **po, Aname *n, Atable* table, int flags); void amlinit(void); void* amlmalloc(int l); Aobj* amlnew(int t); Aobj* amlnewi(int v); Aobj* amlnews(char* v); int amlnext(void); Env* amlpop(void); void amlprint(char d, char *fmt, ...); void amlpush(Env *new, char* name, uchar *ps, uchar *pe); Aobj* amlread(Aobj *o); void amlstackcheck(void); void amlstackdump(void); void amltcheck(Aobj *o, int t, int nullrefok); char* amltname(int t); void amlunbind(Aname *n); void amlunbindtable(Aname *n, Atable *t); Aname* amlwalk(char *s, int how, Aname *dot); void amlwarn(char* msg, ...); void dumpns(Aname *ns, int d, int objs); void dumpopstats(void); void dumpstats(void); void initamlops(void); int isamlbranch(void); Aobj* opcall(Aobj *o, Aobj **uargs); Aobj* pamlblock(int keep); void popamlerror(void); Aobj* nth(Aobj*, int); #define dxprint(...) amlprint('x', __VA_ARGS__) #define diprint(...) amlprint('i', __VA_ARGS__) #define daprint(...) amlprint('a', __VA_ARGS__) #define dbprint(...) amlprint('b', __VA_ARGS__) #define doprint(...) amlprint('o', __VA_ARGS__) #define dprint(...) amlprint('d', __VA_ARGS__) #define xprint(...) amlprint(0, __VA_ARGS__) extern Aname* acpins; extern Env* amlenv; extern Error amlerrstk; extern Aobj* acpilock; OpLnot = 0x92, OpLequal = 0x93, OpLgreater = 0x94, OpLless = 0x95, OpTobuf = 0x96, OpTodecstr = 0x97, OpTohexstr = 0x98, OpToint = 0x99, OpTostr = 0x9C, OpCopyobject = 0x9D, OpMid = 0x9E, OpContinue = 0x9F, OpIf = 0xA0, OpElse = 0xA1, OpWhile = 0xA2, OpNop 9am/aml.ms 644 0 0 56610 11321343207 10506ustar00nemosys.FP lucidasans .HTML "Plan 9 AML Interpreter and ACPI support .TL The Plan 9 AML Interpreter and ACPI support .AU Francisco J. Ballesteros nemo@lsub.org .AB This is a technical note regarding the implementation of the Plan 9 AML interpreter and support for ACPI. It is intended as an aid for those who must change or understand its implementation. .AE .SH Introduction. .PP ACPI is an standard for configuration and power management on the PC. In ACPI, the platform (motherboard and devices) provides tables that convey configuration information to the OS. .PP Besides the static information found in tables, ACPI configuration includes bytecode for a machine language known as AML. The OS is expected to execute AML code both to initialize ACPI and to trigger actions on devices (e.g., to change the power state). Also, some hardware interrupts may require that we should call AML to handle the implied events. .PP AML is the machine language for an abstract language known as ASL. It turns out that AML is more a representation of ASL than it is a language for a machine. In understanding what the interpreter must do it helps to look at ASL definitions for the operations considered. AML is close to them. .PP The language operates on objects that live in a tree-shaped namespace, global to AML. The AML ``machine'' does not have a general purpose stack. It operates on the name space and has ``builtin objects'' that represent arguments and local variables. Thus, thanks to the AML specification, a name may represent a method object, or may not; when it does, what follows in the code may be arguments for it. There is no operation for calling a routine. In general, it is not feasible to say if a bytecode is an operation or raw data. .PP The name space is initialized as a side-effect of loading .CW DSDT and .CW SSDT tables (and evaluating AML code on them). .PP Names are four characters, but searching rules for names are not what could be expected. For example, a relative name may be the resolved as the aunt of dot. As another example, names .CW _OS_ and .CW _OS are considered to be the same. The global .CW acpins keeps the root of the name space. .PP A name refers to an object (perhaps none), and besides that, may or may not have children. So, names are both like files and directories, depending on the point of view. This is an example (portion) of a name space: .P1 /_SB_/PBTN= dev pnp 'PNP0C0C' addr 0x0 { /_SB_/PBTN/_HID= 0xc0cd041 /_SB_/PBTN/_PRW= pkg[2] { 0x8, 0x4} /_SB_/PBTN/_PSW= method _PSW/1 } /_SB_/_INI= method _INI/0 .P2 .LP Here, .CW PBTN is both a device and the root for a (sub)tree of ACPI objects referring to .CW PBTN . Syntax for names in ACPI is actually .CW \e_SB_.PBTN and not .CW /_SB_/PBTN . This implementation uses the more familiar UNIX syntax (with the required ACPI semantics). .PP There are different object types including strings, buffers, integers, references to other objects, buffer slices, register (bit) fields, memory (or I/O) regions, etc. An important object type is called a .I method in AML. It's not a ``method'', but a subroutine that may be called. In general, methods create objects in the name space and use them very much like local variables and, therefore, are not reentrant. .PP Execution of AML operations is performed by using (and modifying) objects in the name space. There are control operations, arithmetic operations, and operations on objects (roughly speaking). There is no operation to call a method. AML is not a language for an abstract machine, but a compact representation of a source language. Thus, evaluating AML is similar to interpreting source (ASL). For example, to call a method, the name is placed into the bytecode and then the arguments. Depending on the context, a name found in the bytecode stream may imply a method call. .PP Most operations copy objects and may lead to either explicit or implicit type casts, which must follow rules indicated on the spec. Beware that casts are in many cases not what could be expected. For example, obtaining an integer from a string differs from what .I strtoul would do. Buffers may be handled as integers or buffers depending on their size. Converting a buffer to a string and writing it into a register does not produce the same effect than writing the buffer. Etcetera. .PP To look for further information on ACPI, ASL, and AML it helps to consider: .IP \(bu .I Advanced Configuration and Power Interface Specification. 4.0. June 2009. .br .R This is the ACPI specification. Chapter 5 is a description of the various ACPI tables, the namespace, and important objects. Chapter 18 is a description of ASL. Chapter 19 is a description of AML (or, rather, a description of how ASL is encoded into AML). Section 18.2.5 lists the different objects and type conversion rules. .IP \(bu .I OpenBSD ACPI implementation. .br .R See .CW /sys/dev/acpi in the system kernel source. Many operating systems use an Intel provide implementation for the interpreter. However, this one is easier to understand and can be used as additional documentation for ACPI and AML. .LP The AML interpreter is currently work in progress, and can be used as a user program to parse and execute AML code as dumped by a kernel during boot, using a temporary .I acpi device for such purpose. .PP When ready, the interpreter must execute at .I links time during boot, or anytime before configuring devices. By that time, there are no processes in the kernel. .SH Implementation .LP The important files are: .IP \f(CWaml.h\fP .br It defines the data structures used by the interpreter. .IP \f(CWaml.c\fP .br A driver for the interpreter. Most of it would go when in the kernel. It contains the main program and code to translate the dumps made by the kernel to binary data that can be interpreted as AML bytecodes. .IP \f(CWamlrt.c\fP .br The run time for the interpreter. Includes helpers and several important entry points to evaluate AML code. .IP \f(CWamlop.c\fP .br Implementation for AML operations. .IP \f(CWamlconv.c\fP .br Implementation for type casts and object copying. This part of the standard is a tangled web and its code is kept appart. .IP \f(CWkernel.c\fP .br Compatibility tools to keep the interpreter code as ready for the kernel as feasible. .IP \f(CWacpi.c\fP .br Contains tools that should be provided by the kernel ACPI driver. They are not part of the interpreter but may be necessary for this program. There is a version for the kernel in .CW 9pc/acpi.c , intended to replace this file when the interpreter gets into the kernel. .SH Names and objects .LP The global .CW acpins keeps the root of the name space. This is the data type for a name: .P1 struct Aname { char s[5]; /* NNNN\0 */ char* path; Aobj* o; Aname* parent; Aname* childhd; /* first child */ Aname* childtl; /* last child */ Aname* sibling; /* pointer to next sibling */ Atable* table; /* [ds]sdt table id; for unload */ Aname* mnext; /* in list of per-method binds */ }; .P2 .LP The list of children starts at .CW childhd . The actual name is .CW s , but the entire .CW path is included as a convenience. The object bound to this name, if any, is kept at .CW o . .PP The data type for an ACPI, or AML, object is this: .P1 /* * ACPI Aobject. */ struct Aobj { Ref; int type; /* for this object */ Aname* name; /* ACPI name if bound or nil */ Aobj* next; /* in list */ .P2 .P1 union{ u64int ival; /* doubles as handle */ char* sval; /* strings and names */ Nbuf buf; /* byte buffer */ Nslice slice; /* buffer slice (bits) */ Nreg reg; /* memory, i/o, ... region */ Nfield field; /* slice (bits) of object */ Nref oref; /* local, ref, arg */ Nlist list; /* packages */ Nscope scope; /* for parsing */ Nmethod method; Ndev dev; Npwr pwr; Nproc proc; Nthermal thermal; Nmutex mutex; Nevent event; }; }; .P2 .LP Objects may be kept in a list of free objects (when free) or in a list of objects contained in a .I package object (an ACPI array). They are reference counted (including as references those from names, from other objects, from the interpreter stack, etc.). .CW Type may be: .P1 Onone = 0, Ofree, /* debug */ Oslice, /* buffer field or slice */ Oscope, /* scope */ Omethod, /* method */ Odev, /* device */ Oproc, /* processor */ Opwr, /* pwrrsrc */ Othermal, /* thermal zone */ Oref, /* ref to object */ Olocal, /* ref to local object */ Oarg, /* ref to arg object */ Ohandle, /* definition block handle */ Obuf = 'B', /* buf */ Oevent = 'E', /* sleep/wakeup */ Ofield = 'F',, /* register field */ Oint = 'I', /* integer */ Oname = 'N', /* a name */ Omutex = 'M', /* mutex */ Opkg = 'P', /* package object (array) */ Oreg = 'R', /* data region */ Ostr = 'S', /* string */ Odecstr = 'D', /* like string, but bytes from buffer */ /* values are printed in decimal */ Oany = 'O', /* any object after resolving names */ .P2 .LP .CW Onone is uninitialized. .CW Ofree is used while the object is on the free list (for debug checks). .CW Oslice is called a .I field in ACPI, but there are three or four types of fields it is easier to understand this object as a slice of a buffer. We use .CW Ofield for fields that identify bit-slices of other fields or system memory and I/O spaces. .CW Opkg is actually an array. .PP A slice value (buffer field in ACPI) is defined as .P1 /* * buffer field. slice of bits in buffer. */ struct Nslice { Aobj* src; /* source Obuf object */ uintptr off; /* starting bit index */ uintptr len; /* number of bits */ }; .P2 .LP We keep offset and length in bits because .CW Nslice is used to represent several ACPI field types with bit/byte/... granularity. A general purpose .CW bitcpy function in the interpreter is used to move bytes when feasible and bits otherwise. .PP An address space region (memory, I/O, PCI, etc.) is represented by .CW Nreg : .P1 struct Nreg { int spc; /* io space type */ u64int base; /* address, physical */ uchar* p; /* address, kmapped */ uintptr len; uint tbdf; /* pci only */ }; .P2 .LP All other field types in ACPI are represented by .CW Nfield . This represents a slice (bits) of a region or another field and is the main object used to perform I/O. .P1 /* * bit field for a region, or banked-register bit field, or * (index,data) register bit field. */ struct Nfield { char* fname; /* field name */ int accsz; /* access sz in bits for source*/ int locking; /* needs locking */ int update; /* how to update */ uintptr off; /* in bits */ int len; /* in bits */ Aobj* reg; /* region the field is for */ Aobj* bank; /* bank name (for banked field) or nil */ Aobj* bankval; /* bank value (for banked field) */ Aobj* idx; /* index reg. (for index field) or nil */ Aobj* idxval; /* value for index */ Aobj* data; /* data (for index field) or nil */ }; .P2 .LP To make things uniform, it has bit granularity so we may forget in general if it is a bit or byte aligned field. .CW Accsz dictates the number of bits to I/O at a time. .CW Update dictate what to do with remaining bits in the source object upon writes to the field (preverve them by reading them before, write as one or write as zero). There are two kinds of fields: .IP "\fIIndex fields\fP .br A slice of a .CW data register available after setting an index register, .CW idx , to a value. .IP "\fIRegion fields\fP .br A slice of a region, perhaps requiring a write to a .CW bank register before using. .PP Scope objects are not ACPI objects. They are used to represent a scope (also introduced by an AML .I scope operation) as an aid in parsing. .P1 struct Nscope { char* name; uchar* pe; /* end of AML for it */ }; .P2 .LP A method including information about the number of arguments and the portion of AML implementing the method's body. .P1 struct Nmethod { char* name; int nargs; /* arity */ int isexcl; /* is serialized? */ int synclvl; int trace; /* 1: on; -1: off; 0: as you were */ uchar* ps; /* aml for this method */ uchar* pe; /* end of aml */ void (*f)(Aobj*); /* C implementation for builtins */ }; .P2 .LP There are other objects that should be either self-explanatory or uninteresting by now. .SH Devices .LP Devices include information about resources for them. They are kept in al list of .CW Res items. .P1 /* * ACPI device as seen by drivers. */ struct ACPIdev { char* name; char* pnpid; /* pnp id */ u64int addr; /* address */ Res* res; /* current resource settings */ }; struct Ndev { ACPIdev; uchar* pe; void (*handler)(Aobj*, int); /* for events */ void* aux; }; .P2 .LP A resource is described by this data type: .P1 struct Res { Res* next; /* in resource list */ int type; /* resource type */ union{ struct{ u32int irqs[16]; uint flags; int idx; /* index in src */ char* src; /* where resources come from */ }irq; struct{ uint chans; uint flags; }dma; .P2 .P1 struct{ u64int min; /* address of the range */ u64int max; /* address of the range */ uint align; int isrw; /* memory only, ro or rw? */ }io, mem; .P2 .P1 struct{ u64int mask; /* which bits are decoded */ u64int min; /* address on the other side */ u64int max; u64int off; /* translation adds this */ int idx; /* index in src */ char* src; /* where resources come from */ int flags; int rev; /* acpi revision id or 0 */ u64int attr; }as; Gas field; .P2 .P1 /* non ACPI resources; invented by us to keep them here */ struct{ u64int addr; int pin; char* src; int idx; }prt; /* pci intr. routing, from _PRT */ }; }; .P2 .LP This is a ``compact'' representation of the ACPI data structures for resource settings, parsed by the implementation. A cleaner structure should be used instead, once we know which pieces of information are really necessary. .PP There are other objects for processors, thermal zones, mutexes, etc. Just keep in mind that they are not devices in ACPI. .SH The interpreter .LP The AML interpreter is a table driven implementation. It is a single thread that loops fetching operation codes and executing operations according to them. Operation codes may be one or two bytes. Arguments depend not only on the operation code, but also on the state of the interpreter. .PP A table, .CW amlops , provides the implementation for each 1-byte operation code. Another table, .CW amlxops , provides the implementation for, so called, extended operation codes. This is an excerpt: .P1 [OpZero] {opnum, "zero", Dry, ""}, [OpOne] {opnum, "one", Dry, ""}, [OpAlias] {opalias, "alias", 0, "On"}, [OpName] {opname, "name", 0, "nO"}, [OpMid] {opmid, "mid", 0, "OIIt"}, .P2 .LP Each entry is defined by this type: .P1 typedef void (*Opx)(void); /* * AML operation */ struct Op { Opx x; /* implementation */ char* name; /* debug */ int flags; /* call on dry runs? Print { } on dumps? */ char* args; /* arguments, for AML parsing */ int ncalls; /* how many times used */ }; .P2 .LP The implementation assumes that each operation is responsible for building an object (in general) and may require arguments to perform its task. A string in each entry encodes argument processing so that, after fetching an operation, the .CW pargs function may parse as much AML as needed to build argument objects as dictated by the argument string. For example, .CW opalias requires an evaluated object and a name, as encoded by the string .CW "On" ''. `` .PP In the argument string, a lowercase refers to an object of a particular type with no evaluation performed on it. An uppercase refers to an object that, after evaluation, has a particular type. For example, .CW S '' `` means to parse an object, evaluate it, and obtain a string object (perhaps by doing a type cast to string). On the other hand, .CW n '' `` means to parse an object that should be a name. .PP These characters may be used to specify arguments: .P1 Obuf = 'B', /* buf */ Oevent = 'E', /* sleep/wakeup */ Ofield = 'F',, /* register field */ Oint = 'I', /* integer */ Oname = 'N', /* a name */ Omutex = 'M', /* mutex */ Opkg = 'P', /* package object (array) */ Oreg = 'R', /* data region */ Ostr = 'S', /* string */ Odecstr = 'D', /* like string, but bytes from buffer */ /* values are printed in decimal */ Oany = 'O', /* any object after resolving names */ .P2 .LP These other characters, quoted from the implementation, may be used but do not correspond to any object type as known by the interpreter (they build objects, but do not represent different object types): .P1 /* * - 'l' means packagelen * - 'b' means byte, 'w' word, 'd' d-word, * - 'f' fieldlist * - 's' string * - 'n' name or arg or local * - 't' target (name, perhaps created, dst object, nil) * - 'o' any object, maybe a name. * Upper case args are evaluated by pargs to obtain the typed arg. * Other args are not evaluated before calling the operation. */ .P2 .LP But for the pointer to the implementation for each operation and the argument string, the only interesting information in the operation table is a flag to see if the operation must be called on dry runs or not and a flag to help pretty-print (sic) AML for debugging. .PP The main entry point for the interpreter is .P1 char* amleval(uchar *ps, uchar *pe, Aobj **po, Name *n, Atable* table, int flags) .P2 .LP which evaluates AML code between pointers .CW ps and .CW ps using .CW n as the namespace dot for the evaluation. It returns an error string and the resulting object in .CW *po . .PP Another function evaluates a single call to a method object (just a subroutine): .P1 Aobj* amlcall(Aobj *mo, Aobj **args, Atable *table, int flags) .P2 .LP It accepts arguments for the method call and returns a result object. .PP Both functions are implemented by calling .CW pamlblock , which parses (and evaluates) an AML block. .CW Pamlblock is mostly a loop that calls .CW pamlobj , which parses (and evaluates) as much AML as needed to build a single object. .PP Depending on the flags given to the evaluator, it may just dissasemble AML, execute it, print actions as they are performed, and perform various debug checks: .IP \f(CWEdryrun\fP .br asks for a dry run. Usually combined with other flags that dump information. .IP \f(CWEidump\fP .br asks for instruction dumps. This is close to a disassembler. .IP \f(CWExdump\fP .br asks for messages about actions performed. .IP \f(CWEscheck\fP .br asks for full stack checks to ensure that reference counting is correct and to see if objects seem to be correct. This slows down execution a lot, but is useful after making changes to the interpreter, because it is easy to make mistakes regarding object references. .LP When the interpreter finds a method definition is skips its implementation and only records where the code starts and its length. Method evaluation is performed by using .CW pamlblock on that part of the program. .SH Environments .LP .LP .CW Env represents an evaluation environment at a particular scope. Multiple .CW Env s are nested as a result of entering and leaving scopes in the AML program. This happens both to implement actual AML scopes and also to open temporary scopes to save the current environment (e.g., during argument evaluation). All .CW Env structures live in the C stack. .PP A global variable, .CW amlenv , points to the current (i.e., last, or inner-most) environment. This .CW Env can be considered the state of the abstract machine and is implicit to all AML operations. .P1 struct Env { char* name; /* debug */ Env* prev; /* in env stack */ int ic; /* instruction code */ uchar* ps; /* start of what's left of program (PC) */ uchar* pe; /* end of program */ int lvl; /* env nesting level (debug) */ Op* op; /* current op */ uchar* opp0; /* operation address 0 */ Nlist olist; /* list of obj in env */ Aobj* args[Nargs]; /* arguments for op */ int nargs; /* max number of args used */ uchar* argpe; /* pe resulting from arg 'l' */ Aobj* res; /* result from op */ Mframe* mp; /* Method frame pointer */ Aname* dot; /* current acpi name */ Atable* table; /* [ds]sdt table id */ int flags; }; .P2 .LP As said, the implementation looks up an operation code at a time and processes its arguments as indicated by an argument string. Some arguments are simple and are parsed directly. Others are not and may change the environment while they are evaluated. In this case, a nested environment is pushed to protect the environment for the operation (so that its implementation may assume that the environment corresponds to the operation and has not been altered much during argument processing). .PP When an operation is called: .IP \f(CWamlenv->op\fP .br points to the operation being executed (its table entry). .IP \f(CWamlenv->ic\fP .br is the instruction code for the operation (the second byte for two-byte operations). .IP \f(CWamlenv->ps\fP .br is the program counter (the start of what's left of the program). When the operation includes an argument string, it points past the AML code for the arguments. Upon return, it must point past the last byte of AML code for the operation (including arguments and body, if any). N.B.: the implementation for .CW opwhile violates this assumption for a good reason. .IP Operations with implementations that are also used to parse arguments should look at .CW amlenv->ps[0] and not to .CW amlenv->ic to see their IC. That is because the IC is assumed to correspond to the operation itself and not to its arguments. .IP \f(CWamlenv->pe\fP .br is the end of the program. Operations may include a length argument that constraints the program size (e.g., to define a portion of code as the .I then -arm of an .I if instruction). .IP \f(CWamlenv->opp0\fP .br is the program counter for the current operation (the zero address for its AML code). This is important to .CW opwhile . .LP When operations with argument strings are called: .IP \f(CWamlenv->ps\fP .br is past the arguments. .IP \f(CWamlenv->args\fP .br includes pointers to argument objects for the operation. .IP \f(CWamlenv->argpe\fP .br is the end of the program according to the length argument. The operation should not touch AML code past it. .LP Upon return from an operation: .IP \f(CWamlenv->res\fP .br points to the object returned from the operation (perhaps .CW nil ). .LP Arguments and locals are closed when the operation returns. When the environment is terminated (pop) or the next operation executes, .CW amlenv->res is closed. Thus, operations that don't have to return a value may leave an object linked there, so it is collected upon errors. .PP Environments used to evaluate methods include a pointer to an .CW Mframe . .P1 /* * Method activation frame. */ struct Mframe { Aobj* args[Nmargs]; /* method arguments */ int nargs; Aobj* lcls[Nmlcls]; /* method locals */ int nlcls; Aobj** targs; /* tmp. method args while parsing them */ Aname* binds; /* done by this method call */ }; .P2 This is just a place to store method arguments and locals, as well as a place to keep the list of names bound by a method call (which must be unbound upon returns). A method call pushes a new .CW Env (in the C stack) that points to a new .CW Mframe (in the C stack) via .CW mp . Further scopes entered inherit the .CW mp pointer from the parent .CW Env . .SH Error handling .LP Remember that there are no processes by the time the interpreter runs. Errors are reported using an error stack, similar to that used by processes. This is an example: .P1 static void oppackage(void) { Env env; ... amlpush(&env, amlenv->op->name, nil, amlenv->argpe); if(wasamlerror()){ amlpop(); amlerror(nil); /* pass a string to raise it */ } pamlblock(Keep); ... popamlerror(); amlpop(); } .P2 Operations .CW break , .CW continue , and .CW return raise their names and rely on this exception mechanism for their implementation. e ACPI data structures for resource settings, parsed by the implementation. A cleaner structure should be used instead, 9am/aml.pdf 664 0 0 153417 11321343215 10664ustar00nemosys%PDF-1.2 %쏢 5 0 obj <> stream xXrF}/x[f쾬fvax~6PݍCA˳4jikɓ'A~vz&nϹ %k7o"S:4TWywǞ wvqPx8vvP]Yx;4b~Dyz[4v5lpMxwV05Gj{MWSWWwf4;p$QmVwk7[!na|GzY@wA?}:fIDKߏ.Uְ8+nd&в6xdG<=3x,8\{ @pf.sA#ESM%z(`C1,rw7 Bq p&&͋vZթ#chl>!W\H1d[F`MoaSؙ~u ~0x,Go9E(43ӝ'xٛ&Wwg2N}h<y;myO'| C^ (̄'q54Ɉbpn+bl&(4o1-MEAWV'P ܙű吃 x:)X9c_r7]N2WD4|GFk$~胡>Q8 xO-zqАdj|͸,DHO7b]c :"}kZ) |"֖aGPU!m8(cOR2p`}Ր3Q)j]y !hryQ/ϰp}aJ"j{;73Ox»IĶ~YPq:iP!"Oj "-kРv"/;"#Ks BgNsJR8`=" vӑr^(CjJMR$NF1ʬIVMoy Q@hy.S*}1]HD׃r!5*uҖ-X Hf+uᩩϚ3|M,bIQ<ړIL`LZm{U[<=6fϵE3_0rA"\xY*AynWs##I@ M4D;~z,O +1Ȓ.SrGD"Ċ "2P$*Ek(?@§)%( Ա eK\8aڛ@&|P)?8b!`;MnXJ4&([*(]Y{gS]bpah\1os/~vF\Ns^|dрwng8@s-XJc/Wy8BsfF;{eS&g{K3zFw .> stream xYmo_|o ŗ[Q8n"{:<"?,ɻS0#3<3MyEnN^LN[f/)qn.ާ+JO\t~&M|~7blJ՚HHqY3yvfض"E7ooLL =sUDLgfɜàä$!xbӪ\$pS89ת048ySaK_vS9I޴ӏCghjv#K^~w#zY֙麪yk9C;w:* )@j^Ry{h*G_`_Ag%"oZZ #( GhM /M\yCX ͬ.H1˷a˙W!ҟ^@]S ɗ kԣ鄯XlU]}4nz̻*_צW4\):y~$c.gz@T4դ+̐M=vY sԆYKV -~&D2'Y1 ,*L cPY&Y/Az~@yBU7lfvc+DA 7sL..`r.H@8i4"9VaԦ}'NHn&)V2+*a CZR)9$ D`]^ld*lXQLGlwfߙ~\bͷgYcg(*wۑyj*>:=p / cGJc4!Ⱦ}Îo|)֬V,?g"k|YD\,Md-X֞%kTӥ:l%l`7=kĚdnk.sΦhǺzFCʜt::Gf yHPPk9jӵ;Z2f4hgux}q\KB8/%m? q֕hw m'( |Rʴ*oX`n9igz~/$Xz~xɲ+ p*}N%-1$:.qM2wRpFoa3 I9T6v r⠅qpmobGѫۯ9i_iS xuib_-ғO/7Z#>kL-ID &W(T/ D,+G3clT5~P+wQ.|oFChQ&G%E}3-*LӫLU3'"xu 7_\eEʡ+bedZ6$N]=&۞+B#'P*Ǝ@WszChJ`j7_,Z9͔vp=zLkw2(Xy[I>;J#y>T9ٚHA>QZTE! $Xί.ﻙEz%m\rzjco{tꆟ|ʱyyB^4r1 u|6nsǵemѶ뤴MåZ%VѻՁ TXk u1,뤓/nNCq1 WsBR*:)i9v"Zݹ|ȩ3a%;ӋSs/%ŗ](2桏s>edevm0CjX<>r$\d#9:bZ99`Tv3Hm@átYAsGqxMSGKhr:NP2uO'MYS3=Id& T&]a_endstream endobj 20 0 obj 3004 endobj 25 0 obj <> stream xVێ6}W-bՅ5O۠(RAmDHBɛA?)ؙdJA,s=3s,C[6VW!lد݇0n4BeLiVٷ 8ejŇX)[ULU8TQ/J&"~9LH|Wk/)Bnt[ywƮ4 * o۲>@,;eiQpV^=++kA\=70w+pԳ0,% WW5ݕtۦUM A2l `9izb ; E@;@R/e?)F"͹~T%`";w"NZHnv[GVVN)DT Ir8-QcĔ}RdBIV0~crZGzyJVG*J8SZ,e䍁rC?aW7o8ygͳTVۣ;%cQPoz~S>L)HKvkȳ[PUF*,! H7$)V=%5K!sLc  5/`p!9ܞr=5JM#?]y ex-@ BU]-K -SL^.S)NH/P^f԰sˍmwY$HlTOS㦴_$ eO] _ ҀHO8*=M5f`fG-D@N-Wr G/y)(/DdYnmOiGNx^ECCZ:U03ɘ[ɥ MɅAޜ5/>Fdk šJ`/42êaERCz'ڂ{`{_$揳zܼ0'"6={'lg˖}Cv UKM&ŦOf6503 :ʻA _1v8V_q8Sc( σ\N'^)sJf9jyn)󏣺8\ j^dooݣendstream endobj 26 0 obj 1428 endobj 29 0 obj <> stream xXr6)p h!iN2m! 1E*$eW@HOb&t?o;<񕄘_鞼ZNn?g bsO'\}aŜd8"+qI@qs yV4yD B'{ Aޠs0pq&ЍD%pnn.Np_&˛NՍ*+}57{}v긙0dh 쨋:5R>uY]\yJ5 LjyגּSɭ=* F:ͬcrf-q]׺ P m2j8b7(7vq$fNlJ?/S,(5 gucq4ؚ, vZW6eeqU jfWCpD;`-_`*+AWTGa^yE>DgȿmjG٭ js[;\tj՜zdN}7f|ςً~Pg^g!skH\@A0Qن7D39J7VK0QY Yp;ykE%vJ+X4pVX0+V $UFbna$Umg<"=fY* gD6n6 vt{Q# +]RQ`6CB}Ι Qfֳ\*$UʭvPOш-FWB/v3C[tA=?IO8ExќZ ˈP$ZԿ|FIUWi[4 5SPՋiS=VJ".chՎ ޔVL[i(\JCwǽhak~63kɊ1?s;x\D ū35`_s^չ' DzPvb6S9"}ɯ.x]!ߌ+ _e?܋v{7'EC!':aߏlu?_ۆ7ȥ%alǵhu3xoaGNRl[C?C'r~f1V^|tA؊>Kz.4hρ֍4s{-Dqvb4QcJehb=;L䂗*ѣi[_X%E}ki_k,^ۧ] >>_C`B޲atUYzp`Ӛct'ӇCs!v a`\JNK4;nA4=GΣ-> stream xXYoF~ׯط 汼'7( A*aE.%ɐO/b0/ùoLi~G~ǙoX+:WwoVɌ|!0㟙>Wc!.U<7x.[ZyoK߶ !\9&* uy}l>>Py|ބeY<71{ eOU];?@=]Zۖg~2z9_vXo@^IzjV71֞ dI2%ef[Mn AbwחU6?;6}LIwe m,m <8G xLQq~krr̢:ڵKw -Z CW:gQ14bXdnAF<2P+ϥ,wq XUHRmA'u߾YLՑ.&5 o6n;h_ڕOdv)g%S-!wazBs"CaRG.sF04dys,eZfhx_`pEA]zGB ՛8пҾ!^X&t>O%)̮$(gU u%d _w> stream xVK4pm!6jX( j`mAVg'PtKrd6K JERn|$?hh ?EC];O;h$L3/")cd8$yA%d]:E׋D1(K $Iͤ4o+=3u4vp}䋶^ݸgN ?vhY#.wņ/7J93Vcma2 #vX7ȟtymXS "*-U޹⩯yˇNH'B! vlfdtS}'B(Ǒ':`Ź(蜠Xt$(\QZ=kj<1+=L?Ec3xmՕ !lb6Ҡf;=h&gHm$O2BI*EJ Ym@jRЋ7 >|endstream endobj 38 0 obj 1140 endobj 41 0 obj <> stream xWێ6}$o|+wVDPE$H4[\^+-$!}+y3CGvgw`n?eK~Y/fG֛`E\n ɗ;\.\"$%jwX,4x~`_.觋)'KrXH`1'08ʲ;]*%Mm~1A/֗oƓ,.е= j;\xtǎ讫xOGx]L$+ܹsCoykF]WwS>*u8!> Iѥ?@QB>ʭH68ze3[ȏ4\вoFYSk鞳S8;lP}2qf(Uv'΢ET .2w :q $.cOy+/t]Ed7CG&"Zɦ9ԇpt K&Su xW;U&x> stream xXrF}$o(a4hqMM9'8§M X$q\$cX]Borb961̱~/3W,+[?fo.ά63:-7m V{Be{!'U:>wXwvyĒKFwEY#..b$XYYj5[NIJ}S{7q|!m.Wqm|>4xKq8+JU>z*)RekAo-@R%/>P l9(|bHk$3q9T+`I۩TOL=7*OU#qIBMkZmڂqnDUeZZ:p. ?- n>Zy Kv?p5:(eWWl[_g )f+ \iFn)] p4cK |[Waft ccұ0{,܇M7r[{=}MZJq!{,7gwfY>_ϤN87Z[+a4 ;} \M<y%m ;}wr!8 R>/ÝȖ(XxJAv.Iq'\.]DBgX ;KպG H;fe뷃5DxuTy+}A7Vo泴ݗ6W$uaE:ބNG-8Ehcm4@0);#ys1}{;IGQtiu*6npkWN71b.T]yA ShfT G>Qbv i&^pnCLb B` __ڬDh)7H q2Z 6~`їT)hꩌN0UK x90 9D TAF`աK6I~4-\Njs;2EDfO ,n<öDS}6Ռ:0i .\)P∠S&tr}Q,jNu|!P_zt|:p̟v"G=H񓖅C"NN{̦g1AK䅀f1޵$ 'hLyɍN1΅lW<*Q JmT1R"f{' ^lp.Hl.hz5NibJm> stream xXɒF+j a[ۣ ɞq8"P$fm~ѹn8FmY^f7:pU+WWO+> ?I@$a(J~;aw ^Îm}׸;z|mrM| H+9iV!( |٪IuBAW:^{^[_._勵|˫O#ByYCfq]Q:Q/NԪ<fF5 B?G:P1z]Y@7 /_u?e j͟meNuۂ+tHxXbA˥@VHr{47Ưњ3*}أѵSy dx:t{4p.'='\, \~2T+6D v%/^@ ]m<:ì㜶/@ݛNjmT"ŋ.mp;qq SR tiі}ñk0S{ {هKjXc٘9yNh !QYoL'w`0Y\%lpqDLDl<إSA=BgtUKB`!&/Db:)TvanLedkԲlK2¹7sF?61=A[l LQ=ctП ™I@'&ӭ,,NJh]5\5# 7wusq(|ҫ:G\/=ۘ^\s\=B6kSmx\ܶ@W;p@YqK:m~6d$_:z(|FPPmA4ؙ@MC񬺢+Ξww4ƞEට[#R )`#~1$UdMuK_%6tTPb<9w6loӿ0fOIroX+-oʘ(d7kj2r˦/@Irr.;JG<)ƏRY㱝]Ν) > stream xXn67;ݙ6<$:4nP,h=L ;QTQrHyDA #|߃(oyD?a&+)O͛ 7D$ c,؝6 ~&-M]!jo!.$<!H F-evTH߄G0v4eDz84BFn=VSƇkIFTK 4L'`ޤ c*qM*Bӧ-RI]J2F+`LkbAr(uRhXEFXHH}꛺`4FxS3;9أ; ℋ<;H&/H&1rvɖS4/i/gx]AV8N]-M_TхIC)M jJy#K}lj5ޙ7;K3t_9}s;/EJ?beZAX *#9>zR8n'" ^>" דի@^1lj\4Z=y!/Us y`yMGGAyct)?UK&pЧg51nϋe_{ZF eWꓪfP]};oW{}=(QW8d-UbB4]"2?"xtV2xd0&<ʈ^X4xإ<ʌD.ѠxEr0пX%hFw2AYDihb5f@fFxjǝ=t23AU=rlxP8f8N݈-EF<۬|Echz7ӲI4ӗYRJcZ>¤v`{ӊq|I&qQCp(K1:Rb6'"BU3f/'JMof+hf$*;JI4$hQ4p`p*{)a'A5a.0p\D/G<.*(Ϻ^4Wߡ;񱹆 z>/Q@,'ptV tM @| ﺼ@ `+iuEGsV^}S,A [m=d- .j{gۊHcM v]l $-r4_V9 3'3*pcL8H#I2brKG SpRzDv(̤EGp]閍lʆeܸ^%W|ZL!]|kl[?:[@8E>߇eli0˶̷c'uhE=yʴ>ht-e9>iֵיy"CU Z&-%vۂ|#%Vr-krSwAj鱛jc@԰40z؂|}ZXL} (4WYnϽ)) mGYi; XkIxݓ,Ť;ܫm\}?$LLi;# Tu`PSnޢm(Ȣ)39X AlzE]yi +4 scg5i~VND-9= cXղ'RBartLV^ sIHxsu@?qJ#Ã~*hŁZ8X׀Pfa!0_ZKF.,kmlp2C8.]LJp{7Bˆ$ M2 tZ4/k> stream xWo67>-t+D} dC7`kЇj$QŰ?ԇf+@⑼[{>[=rf§Y~.Rw {gAyK`r2g_ۏ c=,xjeVV-k5q9x5]?Bh w#Y:V~YibVls]G&T ѴM^ (ɢPsћeի,/H HSKv x3+ЫsffڔqA{Wj j4B?5N7$zEso%.^ W)!w0ԍod 2tsCz&p~=-.#3>=K2Go SiL.;o E,/fU5횊5dr!F= Ł$u zI ڼb؄B5Xo-ok%LS^hs"Fup(:OP0H m4mQ+Dyoc<据P $$fb:q^r2d(znl )V-gYgu2W$fSdSߦCc[PndaZ|%q1tx/ bSN8I ᳺ0(wUx?GB**mÁ4?ʢ=Z(wn UW uMw=o'h%3]8w$h}u ʠSMOj2uu,&v@RG}Ȱɾ!R?fENA_w91rC2R@.ڽmY߆rJ[ ֐Qet{6}'ؘNxuS/tgo hCϓLJHפ&!Nyyf<\$@@N( 2ǚ u+r؛.e{e]_?+[_n^ lCoo]d Àײ1[{Ia鉲7h>)UF_&s8 _y9k`3%! ΠD.dBi8cZ) -cIBPrBC Gĕf/7RxPF]a?"' `{Ybr1}~ŝ&@6J=M`A!"0X>[Fug1:ƒ*4Gtm\x6ѨN1A(:7lVfB80AQ]ȟO6FWM &A5g?r. ~=hL6^x< ~)f2@b" G^1qܢtn8 0Akif6#@ӚCt4u({1 >3yN+"qQxrVڗcܳ> stream x]PMK0W̱ 66^!v6IiG?:IH{o A"ސ azwܾ0~ĉ )@-N|%yـ8iRNz=9$ 7gm7^Cۄ^ՆY4qH˺ʤ=^ᒫSF9dNYJ~PڅѢ`FV]}0c7ڀ4:z5-qx5+Q F(MiX^guendstream endobj 62 0 obj 277 endobj 4 0 obj <> /Contents 5 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 24 0 obj <> /Contents 25 0 R >> endobj 28 0 obj <> /Contents 29 0 R >> endobj 32 0 obj <> /Contents 33 0 R >> endobj 36 0 obj <> /Contents 37 0 R >> endobj 40 0 obj <> /Contents 41 0 R >> endobj 44 0 obj <> /Contents 45 0 R >> endobj 48 0 obj <> /Contents 49 0 R >> endobj 52 0 obj <> /Contents 53 0 R >> endobj 56 0 obj <> /Contents 57 0 R >> endobj 60 0 obj <> /Contents 61 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 18 0 R 24 0 R 28 0 R 32 0 R 36 0 R 40 0 R 44 0 R 48 0 R 52 0 R 56 0 R 60 0 R ] /Count 12 >> endobj 1 0 obj <> endobj 17 0 obj <> endobj 23 0 obj <> endobj 27 0 obj <> endobj 31 0 obj <> endobj 35 0 obj <> endobj 39 0 obj <> endobj 43 0 obj <> endobj 47 0 obj <> endobj 51 0 obj <> endobj 55 0 obj <> endobj 59 0 obj <> endobj 63 0 obj <> endobj 22 0 obj <> endobj 70 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 12 0 obj <> endobj 71 0 obj <> endobj 14 0 obj <> endobj 72 0 obj <> endobj 16 0 obj <> endobj 73 0 obj <> endobj 21 0 obj <> endobj 64 0 obj <>stream @@ 3IS0eSqo2FC(4H _:E Djg =! pq8R"r4Dő$c&`i3:e9At*D"BZ0eF|F'ct"(8Rh W%@a/p=֡Qc`|u6`X,q:3_ft=;Ԓ]K`X,< MKM. : o<  endstream endobj 7 0 obj <> endobj 65 0 obj <>stream @@3IS0b!iAπ>1>RPGX!RH$]^{ܰÁo8NF9 "r2Sa  asM1p"dDőem Fn1Cl6 Tug2S  "!V.b2|D Y4d'R!@r3ƓI; &\9 @ <D / % @`LHTJ*' 4(*-P~!>&CibMg@xl Qx 8 [ư w (D|$Y|rX{ aY3} 0|Y@{YJı.g/LX } {Qr}p ~ )ZRì|q0ORĘ* . {DW@NDPKY@c}uI砢eB2< ayP?@!]riW]f]g |a]^#RPTc MTGH}8&^N `9$aɲ੶c`!4(/$ A{ɪi`1yY)yKc}:|Z?m$@6wJ 4 @DX!B2{!)`|X'ƈB,cMg <- #0 ,} xL #POzc Fp]*#ȑQVx4t-5Jǻv*="T U3ENv` .CPP/вC)}A1HAP[P ż Gm\k| ;[hu.X=˜IQqDP"> +Ȫb2 0'BCtT#"g1..P=tCP{`EAx GCt A+cE#tmH| [@|,X$g +SlͰPM RD.2[cQaZabKaEp_ +r"j7q4%K{uܖTe5=G84< *`JŖ$( P 0DX;%i&HuZH{kYKb{e("3l ` lU ʙU:S t"İp(-d!h` ^ekysG@ΔGQ+]]PAP XT<"BQEP S"\*.[`-ppV.ĨxAG6.c n6`#(ZQZ H0Xfo5O3hS pRŠlN;b@@an HDXt!@MXPZ@; "x 9` xAq Ĉ/? /(Dۚ88 `)r!L1TT mѾNRگV*ZurX+ buh$YEiU@|~{>Jr$."[))@9^O=Pbb&fmO!@!xբ-^a.5B!Qi\`m>b`\ J>[~ 0U80%*oDžPxֺ+\0 Cw!;^"L@&Hb掵8",uQ,Fepa>@W+}xo.`"adB} 6% H!,_ \+E uL>|QpTA  RaX`>@S͆hg Vܸjaf!l_Ap -xQ&p/(, `z @ h|e x[>>4tsd_F䘏EV   A $,𮓠@)FN)V&܍EIf(bK`!Tbl@Ka1a * A:aaA^`؉ ! T8 ` @Y`*a!>_ P,`vRx! D|  < m .ka$!  !^P.$5 endstream endobj 9 0 obj <> endobj 66 0 obj <>stream @@ 3IS0bIl4q36|O 0o:&/հp = ,89px!h:1Hb9*qo;Am2q\ 1dYkQ3E1mo :j)ZL#.C!J0|D H6C8IC1@g##u*NƲQa<LBpd4 u <D$@`%LH @$3@+`` h0 @ s#xaAN`pIg`$q nx NǠ$bpTv $$(`&%DdPFe8[P|dY@{&& 8|U#8*h~)ǸP Xtcpim O` |XOGYK{G (vE.>d!@D > u GiFEɚ6ˆ'  @&XǸ`0B~)XXm19*0C +q6՘& {g ZYc`fIhG#=}ggb"I2V@|Y{jzlGz '{G `1B3c ;X+ #ԀH}b 1Ev]ED`e\EV%(JdtDLeahY|axE\:!\.~ʼnhz@1E$1D<c(;c"FjfPivq'k(*nfiL`F\z%U@v$!}dXD  Bpjx{̱GaX GMH|@:Рa_Up7ǒ/&>|u * llB04!}Q @@DLb8"xQp G`|g.¤RaT'㸭E1N! >X#E>Ak)7 ChAC~B##XѬ7PT!0xaN@0DLa$E*"`d>ĽH=hT <%xxIH8:>ª@nE)c{ p  #` 9!8iE!Đu >*,"Ea8c@q/"Ha|a j`1W`J-)Cb5-f;G*(NpL8 A. `,4 q8Lb 1f'hY DX)a}a#TN 5x"tJ@=Xc8KQV,؝hNu)YR0([ a(= |6R2F#P +,`q=x1!Za}x# AFcܟ ǸRc x> #>R!"4 {)q9pct |e1lQ93PpZ!TD`aR^( > !4#!`"`Np  ,,h H Tⅵъ5Xb,RQPKQXPa>e a"` H`Li(J,{`"mP p,=_&ŵAm@Xp>H/T C9]z é/2DXbHD (0C"k7hk AQ _ ژ%d{>Мypp}_ a @j K> xIlHnae"P´`P/DI:{@lt092 @:He Qf'¨S aP(EHHKq T< p ptA/J|Bf -8Be)^ ,2(toEa65hC)zژ}zoUp~:P{p*lC("hBLQ67bAH1xuE@g!> ! 2 !J a<n 0|@  @ <a"Z 0 !A!G@ ,VHDVWA.XYYZZ[[\\]]^ @ |AaTa ApaajZAH R *F~ |RhAm@VlHn@ P@|> L`DaPAX$ `& P`   a !2 A,$<!n <(:LjGV` |L&HTJ|jazr`A&tv`pA`h:,P8@0 igT`lh`:BLl 0 *  R `!2 <" A2 dH`?#axab'!R*ErWօȘ1= `@ `2a&,$Hn[ 2 R <_0Thv,QAapAm99MfV|PLR v16*v`vH fҬ lňU T$a@ [ *|aa`S`X`Ja    / & a4 h8)^j~<Ldh3֑dE FHMG2hTLL ZTLa3i4 a܆T F@HHPW |! &~(((((ƌҍhڍhhh)L * `BJ"T~` Ԍ a&aTQlW! FP!a.e! Z|A Rl-_!>p 2 ~ @<  Z!c!:! Z  !`  ?6 HD ؄НGН䞉Y` \ @ڠ  $,6!L>*F%R@j\ d&PP6O)H r a9tz,`/a/!TA|ƹp}N````!a<(P8AZY ``> @hN ňAjL*8`2 ` <@jX**!>@`t p@> @  Z`zĎZ` R!*aPA`:arOt! E ӔX@*[b " TjS2  * AZe`!+!C )a* I awy> v @ v@h a# @a   if"l`>X `؆Y' TAV`<!z A l`dZ a A!`T`a h@D> * p `3 U@lTaj >BA.o4^u)\aa*! !oa8A6gAT!KGz+T @Ǫz!~aPt&*堂  `  ~V~ | @  !pWAA@!:Q .;j.A`<@ a@ CP endstream endobj 11 0 obj <> endobj 67 0 obj <>stream @@ 3IS0eSqo2F4L _:/SF  5t 'OHE p+ @3 @p;@C@p$K@DS@pd[@c@pk@s@p{@A@p$ C@DE@pdG@#I@p+ br9 "bȀn1a@r4Cs2S  "!R, 6z"2H``r` mn@am Bp5 hk `C?An0C44DXi,FQ.g HHe$/ðpi&H'mf1Dpo"аkE!B8ˁR0AˁnG!4!@R< &! h0'aZ8 xÈ(^9 0vc@H$C* %hf; b`L$PE$`F$pG$H$I$J$K$L$M$N$O%P%Q% R%0S%@T%PU%`V%pW%X%Y%Z%[%\%]%^%_&`&a& b&0c&@d&Pe8.v#qA@!Ca `( gp.>a*DA14DXxv'Š>_ F"[ >*"S 66|1!{:# @8 d; & P5%<pRv A;Gb|.~{`*= {` >#&qBAl0^=rx{%^b :c&NXeT{p**OaB*ĸb,O10D{Ep?<ʆS`K i) H€|`"HaTBĨda|EChm?`Ȃ!PR@%0 ,5 0(1x;Pð 20'f@x4C:88 &A Q)Xprp= w&gbR0+H(}>A7 12vHMGc0>GH| *#0_QlB If? P|8@Q'w}qV8 ݏ~>H0Ǥ>ck%-I?Y+cWQuvc0{Q@Ã=W {bK0È ((֐(>.DȲ¨V qX3*C zz' V >/_[LX\ p Af? a! ! ` 4a  A ! AA @ Fh!f`@a0!PR-`$! İ!ܚB*   n@ d !b`J6*af` d  M!`L  a`xA6FP8AAg` 4 aN <@2z@A!2 AxAz a:kT: z8 1D~*&@   $, 4< Ĭ̭ ԭܮ 䮊 ޡ  ,0!TA^1xl,a!`$n&.L  @`(hb!2 A(A @!&|A p2). !)Ua[R%A Uu\ J 58! ``I !&˾$~aAbcM a aAD`4 A@*bAv z H ^`T f @ ED``.¬.aAg'>N8!6A8aatAb&Hb`h 8`J!x`>a(,.l @ @w> * ABdf!B` rch}l ؽG0AA WGT1qkQQ R !!"%", `@ O 10!2a#qX62,»!FzD``V> ! `aNAz<]aD~T!paz!l!Fѐ` @@e@ ad+H`ĺdID *  a3,AhG]͒`V VԠ+!B}<Ȅa@h`*|[Ԅ)i *\aΡ>A `Ln `j tvbTKzzbuzXؗ !*` A.ta:n2aBaaa^ba)+1Xw@8 v|V !z8daPWaXހh  ( !)aAnXᏐ UA Y) ٓAyF YJaZ  aRaL;=L 'w23&Q(}5XRխZ⑮!p\R LLt Q#!9FThJL !`"!*7J * )f@aC` @ `͟> < $d~/@ tr H@@K@iתakT'-ݭj,٭-ڡ@m-ۭ-dz9#OD@#`*!>,R!@xx aT ^ 2n ,8a$^jR @   «FAA5a  :?& 8T `a  4*<U Ai 'I-L*wANA_YY~B ~\` =2ӅA2\Սm bu!o@ >` `!$ @ .4΀`atfA@v`A!bјya eˮ,3Glz )IAbAP>( !`8 `@ @laBb@>ܢ`  r  2Ah:AX2aK*na[ƠAʙA ؀K @ `  (M蔪-{EOR&:,ՓMz=؏ QO݀ { `ELEHSz9Mȳ,+A:CJ]EkZW%h T3ݎ89 E Vĩ:oUl?cQxK{|.r*%~:^r8iͤ;[yi׬ǺE| c׈\&{_@)bo"gqRpNe@ؘOeI<1D2E'I[O%bׇVlW ȈN`.T:Lę>IyFE`AB. d12> 9Sd~Ex0/c? EqM-ɉbaaqX {'LlAnIQ8B\cz5X cJ {9Fe {A$& 1{G*y20&2f}"{.uY@'bt1p[e6^g"Y"["8Ed(:c9CA6]DL[DSgTj3a>y&'p» fV| l !Dsx٬}-ZPI,q"|l!rĦ qt)Zh([/s vjōbZ{.d^p͊2mx|p*K@\e!R*B DYE$XId Dt>%nce8REhN XEd<IC0EYMFEISDMdE@`FE210bVf t^n$y(IĨ>7@8D3ɘi$2aXׅ1JX>@0P/L6B"!D' @|q@1CE<.Űb> endobj 68 0 obj <>stream @@ 3IS0eSqo2FC4H _:E\ Dj =!X8zN)1Hb91d@I7 9L惡e9NGc)] C)D6bq1@ f 88~y{ٗ {U'kT hTn3o EϗRtZJ'DM#I#dp#6Ϋ9&|ԴЫ}&_ʶG3ʺ@ (A!hd*D u3K.IKu`#xdd1LR endstream endobj 15 0 obj <> endobj 69 0 obj <>stream @@3IT< se9Agl-a|*0(tMAq|/u=cpsC71S9 "r3U*Ag2 q@H7 Sdn1q@Ym j0Tcqf2 Da@Rӎxd`e2 Pp!El># 8A< #ѼN!ċD!P"cG:JdI;ʧsax6HX" 0N3Rظ]Kx=>-x"p(( (0#$8*H@*PH+XP+`^@X -h!PjS2@`8 \(*G `N !f@(l`0 `1 8`@`HR P2Q\X@`~ a, h"`ihpqy@f  | L   ` ^ `& Ȳ JIcit*gcv  9?z} 0&\fJcyϙ቎{gPI"|H*~`(8X {fJg`*FǸ,}`)L*c%z)5}Byݸ =ٞ=YT'<~ A z3sA!'?X. 9vkfQSe`LDHXepi8T{ 9k*` "d.v" d A` x5qR1]%!itd^=la42Dp[GX%a:0Bn,?=lX a$ŸHKT&@@>  haC0 D8Ǹ BAJĀB JPיt4j8[<~ @UsՂFv(4҂xk2<{>;$~@ 4[_lbw!y=q=`Tag3:"a410hnLqBCЊ4G `| FB,^v(faJ(>Wu.Gt0'H}`? "t!΂q@#CD81;DBT> ΄g@6G%BcpkSK~}>߀5u<Ձ)'$䆝f0iEhC\"rz6^>t%#|!Z%n (bT5$Tc&>{*.DȨ|T 4&`P0|(H* =#_ uRp 5)a=54Cjᅚ*jV8Oj EK3G tLye G 0WH a<} XP !4#\IK B=ȿBDLP<)"Oà{a? dY{L+D0|Pa.!8Ͷ03@z/MK&7<A!G ua'$p&;ps" QVE{*ٓxZ`Cn |7pPa 11ŀΌa05!`/ ^nRPFmc{**_#0 .B,Y \6߀;V%p2Fb !%Ā ip 5`"&,=n %,0ò31lٜ26^F)>}R?H #_pA8TgC}=ǸL7mp`=ØL,Z\'`{:﯅= 럺M@{ Ǹz31O`Q?k0Uȅ^<40c < 0¨,Z[ p* 8<9t{HkK[7pHPs!K0#ZaUMH^ em-V\ .%ƹ(z T!CA|,`8zJ a:"a:D!blaH|a Aa Ơ`}A !$Ÿ`a>ΉXu`$ tlla`ĥN NnAraal T AV !.`B "ja@| d > @p`F:z zg8a Fh Fgmhsf!IVܲ0kzt&zz)^xa.86aVaJπIp  laRjXЊ`ڧBNl`JarzA z ` f:!\ >A4|@* ` @`  aa Ad<`i `4*2DR,PB- |!H atvXA A2a 0z~ `, @8 3&A<AZj A| jjay^`j>pj6Obk1|k@J>btA  A!*: @af|T!aaz. l`@@ʟJ|༩@❁`  2  J DV! rL'6̇a*g``F ` ` ĩ >~||}}~~ fJ6P Av`@ k VjjV'"oV8s*  F ` iklag!n"@`)N+ČL~,X<bljtA  a!Uv࢝~ !Ɍ<ʬ+d& A, A$ !$  L. 8A(8$>hp;1l VxfΆ ``0!D!a ! ZkQ*s]gPH|H&VNa  Pj+pA@kA&xzb( a !`k vA:4B!` @aaA,H !p!R P2A!ܕ<)J@`j~nA_ <඀ i\la0`gbQa^AG<» p }5tj Pzj *   A29i! |rԂ 4 ` T!jtaaAŀFsUl`OV!m!⬋a*ol. ` ! vA   0i/d  aAolvaX~[b.J<uf`lupsԴtƨ6f  ! @GJJE~GXu A PVxr x yl` jZ j'@ua$ n+0AH"f AXr*( @ Ʈ tgf.<> @ zB AB ^ \ZuakO< h p axnrTQ*t> ~tjH.g p [   ] zu` Rr Ѭ"ӫ +kl lƼh fr X`0 & a> AD !F (a !@pV!0Ah ru(@u `\,A6! RA@aV@>@!.(Y@CJA Y a@ Aua#Aߞ7"A.Tj2v!\ Pm`dd `Hh gVi ``zjbr hljAV`jpQ<ǮL\xafN `>4jYQw!i0A a ! fj   @ A@`` zXxѳnj\ f`!zz  zx<2a H 58 ba\ЀJ'l-&ҭ.%@a '"K(`d xbYh^n*V~?~O<'V @n$sF)hLa*! ` , r R"LaO`x A ~p .m3>pKx ubM= Eu~  .ill!dTT!f7 ;-!@Fatb!a 0A * Aaa0a1(̀TD` %.? z>0!)``!hm@$a !p` s@b J{`g\&d8u/;"j"C%&w9/pc2/U (i+L -' Pg e( FOX %P>_ Es N?VrRycBlp lUkW I@ސsЍ)}cQ,{}#5$<" MX!৹u^c^/&;.P?ME.{7pBWem'۳kz}ER|+m;u$roq[ӆUrU !©W:N@6Z"a}rED@@ Bèe^c EO RDLWeI0XeF$@H# xH"@c9/ (.b}V{ZILdyfMq0;br#cp+L@}RC8?!LEA|UrT ".rsoKؙbe l)Z(/ $B9.{Y>L%9& X<X=C:Hl =4C3A*e*N%YXWExWep0reEB| E ʒ!ũ&@dx)J g6"{GX}dga&*Y$&QD-GJJY\E|3!P,2cd)D 9"y,Gp2LLgQ01 c>3#p BqũXEP D]> JF$xH0`1|@AJDAvDmY -OLfJ@@>'L> *GPNqD%PVNx+7`#b$@p!L\ 2&DdP<#D٫X-xp|@@ C~ |!77@ob&ElL q<+=r 'ЧpJ4>,CLkqL7ʡ, +@1R`}a(CȒ2lh`rx?ئjcO 2YK:fTiڛU) H\#B66:b"QE@etH1 DXaD=#t. E;Ø#& !$#gv! @0BtD (HByՈ:8sK,ȒT,vx ` vY" 3(cG F3^,CU aK@0mx t< $Cb49`};zprAX;86Cc @C\s! A QPWb,| >\p.%Z0^"|*?Ey xiv/ +(Clc|0 b2hR@ ,Dط >%ĨNXWPAW $E@2p7{7I C<c`Q/ExcD rB 5@<Xq ,-%Xh9(d aReO&b |-FA%PB8YۆA>!c&,v!| !;fhE>qCpa4 B,9 '{CP `'E"XO !(%Đ`I (q"(L0 @"hDP4Ÿ`x` pg%< `;W "4>"<Èqa(Մ30b!2{,?"C `CADXwmX *MY6NrijpNf.-enJz4ɤ{m.%eP Ip$r5$?C 8 J x 絇 #B3JF,0>v`n`x+Q@OB!NܷXL:`TN`Q(Rf Y@@EIR`BWF bGFO@xe۽'[h"@>>endobj xref 0 74 0000000000 65535 f 0000024296 00000 n 0000053311 00000 n 0000024159 00000 n 0000022433 00000 n 0000000015 00000 n 0000002292 00000 n 0000028560 00000 n 0000025322 00000 n 0000031910 00000 n 0000025719 00000 n 0000036896 00000 n 0000026141 00000 n 0000044042 00000 n 0000027152 00000 n 0000044720 00000 n 0000027406 00000 n 0000024344 00000 n 0000022575 00000 n 0000002312 00000 n 0000005388 00000 n 0000028000 00000 n 0000025077 00000 n 0000024418 00000 n 0000022719 00000 n 0000005409 00000 n 0000006909 00000 n 0000024503 00000 n 0000022863 00000 n 0000006930 00000 n 0000008703 00000 n 0000024555 00000 n 0000023007 00000 n 0000008724 00000 n 0000010586 00000 n 0000024609 00000 n 0000023151 00000 n 0000010607 00000 n 0000011819 00000 n 0000024663 00000 n 0000023295 00000 n 0000011840 00000 n 0000013281 00000 n 0000024737 00000 n 0000023439 00000 n 0000013302 00000 n 0000015514 00000 n 0000024800 00000 n 0000023583 00000 n 0000015535 00000 n 0000017916 00000 n 0000024854 00000 n 0000023727 00000 n 0000017937 00000 n 0000020212 00000 n 0000024906 00000 n 0000023871 00000 n 0000020233 00000 n 0000022043 00000 n 0000024971 00000 n 0000024015 00000 n 0000022064 00000 n 0000022413 00000 n 0000025034 00000 n 0000028221 00000 n 0000028856 00000 n 0000032235 00000 n 0000037451 00000 n 0000044296 00000 n 0000045302 00000 n 0000025236 00000 n 0000026713 00000 n 0000027315 00000 n 0000027899 00000 n trailer << /Size 74 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 53421 %%EOF 5 /Ascent 783 /CapHeight 783 /Descent -205 /ItalicAngle 0 /StemV 108 /AvgWidth 723 /MaxWidth 723 /MissingWidth 723 /CharSet(/L/four/A/minus/question/y/n/c/M/B/five/z/o/d/N/C/bracketleft/six/p/e/Z/O/D/backslash/q/f/P/eight/E/bracketright/r/g9am/aml.ps 664 0 0 251674 11321343214 10540ustar00nemosys%!PS-Adobe-2.0 %%Version: 0.1 %%DocumentFonts: (atend) %%Pages: (atend) %%EndComments % % Version 3.3.2 prologue for troff files. % /#copies 1 store /aspectratio 1 def /formsperpage 1 def /landscape false def /linewidth .3 def /magnification 1 def /margin 0 def /orientation 0 def /resolution 720 def /rotation 1 def /xoffset 0 def /yoffset 0 def /roundpage true def /useclippath true def /pagebbox [0 0 612 792] def /R /Times-Roman def /I /Times-Italic def /B /Times-Bold def /BI /Times-BoldItalic def /H /Helvetica def /HI /Helvetica-Oblique def /HB /Helvetica-Bold def /HX /Helvetica-BoldOblique def /CW /Courier def /CO /Courier def /CI /Courier-Oblique def /CB /Courier-Bold def /CX /Courier-BoldOblique def /PA /Palatino-Roman def /PI /Palatino-Italic def /PB /Palatino-Bold def /PX /Palatino-BoldItalic def /Hr /Helvetica-Narrow def /Hi /Helvetica-Narrow-Oblique def /Hb /Helvetica-Narrow-Bold def /Hx /Helvetica-Narrow-BoldOblique def /KR /Bookman-Light def /KI /Bookman-LightItalic def /KB /Bookman-Demi def /KX /Bookman-DemiItalic def /AR /AvantGarde-Book def /AI /AvantGarde-BookOblique def /AB /AvantGarde-Demi def /AX /AvantGarde-DemiOblique def /NR /NewCenturySchlbk-Roman def /NI /NewCenturySchlbk-Italic def /NB /NewCenturySchlbk-Bold def /NX /NewCenturySchlbk-BoldItalic def /ZD /ZapfDingbats def /ZI /ZapfChancery-MediumItalic def /S /S def /S1 /S1 def /GR /Symbol def /inch {72 mul} bind def /min {2 copy gt {exch} if pop} bind def /setup { counttomark 2 idiv {def} repeat pop landscape {/orientation 90 orientation add def} if /scaling 72 resolution div def linewidth setlinewidth 1 setlinecap pagedimensions xcenter ycenter translate orientation rotation mul rotate width 2 div neg height 2 div translate xoffset inch yoffset inch neg translate margin 2 div dup neg translate magnification dup aspectratio mul scale scaling scaling scale addmetrics 0 0 moveto } def /pagedimensions { useclippath userdict /gotpagebbox known not and { /pagebbox [clippath pathbbox newpath] def roundpage currentdict /roundpagebbox known and {roundpagebbox} if } if pagebbox aload pop 4 -1 roll exch 4 1 roll 4 copy landscape {4 2 roll} if sub /width exch def sub /height exch def add 2 div /xcenter exch def add 2 div /ycenter exch def userdict /gotpagebbox true put } def /addmetrics { /Symbol /S null Sdefs cf /Times-Roman /S1 StandardEncoding dup length array copy S1defs cf } def /pagesetup { /page exch def currentdict /pagedict known currentdict page known and { page load pagedict exch get cvx exec } if } def /decodingdefs [ {counttomark 2 idiv {y moveto show} repeat} {neg /y exch def counttomark 2 idiv {y moveto show} repeat} {neg moveto {2 index stringwidth pop sub exch div 0 32 4 -1 roll widthshow} repeat} {neg moveto {spacewidth sub 0.0 32 4 -1 roll widthshow} repeat} {counttomark 2 idiv {y moveto show} repeat} {neg setfunnytext} ] def /setdecoding {/t decodingdefs 3 -1 roll get bind def} bind def /w {neg moveto show} bind def /m {neg dup /y exch def moveto} bind def /done {/lastpage where {pop lastpage} if} def /f { dup /font exch def findfont exch dup /ptsize exch def scaling div dup /size exch def scalefont setfont linewidth ptsize mul scaling 10 mul div setlinewidth /spacewidth ( ) stringwidth pop def } bind def /changefont { /fontheight exch def /fontslant exch def currentfont [ 1 0 fontheight ptsize div fontslant sin mul fontslant cos div fontheight ptsize div 0 0 ] makefont setfont } bind def /sf {f} bind def /cf { dup length 2 idiv /entries exch def /chtab exch def /newencoding exch def /newfont exch def findfont dup length 1 add dict /newdict exch def {1 index /FID ne {newdict 3 1 roll put}{pop pop} ifelse} forall newencoding type /arraytype eq {newdict /Encoding newencoding put} if newdict /Metrics entries dict put newdict /Metrics get begin chtab aload pop 1 1 entries {pop def} for newfont newdict definefont pop end } bind def % % A few arrays used to adjust reference points and character widths in some % of the printer resident fonts. If square roots are too high try changing % the lines describing /radical and /radicalex to, % % /radical [0 -75 550 0] % /radicalex [-50 -75 500 0] % % Move braceleftbt a bit - default PostScript character is off a bit. % /Sdefs [ /bracketlefttp [201 500] /bracketleftbt [201 500] /bracketrighttp [-81 380] /bracketrightbt [-83 380] /braceleftbt [203 490] /bracketrightex [220 -125 500 0] /radical [0 0 550 0] /radicalex [-50 0 500 0] /parenleftex [-20 -170 0 0] /integral [100 -50 500 0] /infinity [10 -75 730 0] ] def /S1defs [ /underscore [0 80 500 0] /endash [7 90 650 0] ] def % % Tries to round clipping path dimensions, as stored in array pagebbox, so they % match one of the known sizes in the papersizes array. Lower left coordinates % are always set to 0. % /roundpagebbox { 7 dict begin /papersizes [8.5 inch 11 inch 14 inch 17 inch] def /mappapersize { /val exch def /slop .5 inch def /diff slop def /j 0 def 0 1 papersizes length 1 sub { /i exch def papersizes i get val sub abs dup diff le {/diff exch def /j i def} {pop} ifelse } for diff slop lt {papersizes j get} {val} ifelse } def pagebbox 0 0 put pagebbox 1 0 put pagebbox dup 2 get mappapersize 2 exch put pagebbox dup 3 get mappapersize 3 exch put end } bind def %%EndProlog %%BeginSetup mark % % Encoding vector and redefinition of findfont for the ISO Latin1 standard. % The 18 characters missing from ROM based fonts on older printers are noted % below. % /ISOLatin1Encoding [ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright /parenleft /parenright /asterisk /plus /comma /minus /period /slash /zero /one /two /three /four /five /six /seven /eight /nine /colon /semicolon /less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space /exclamdown /cent /sterling /currency /yen /brokenbar % missing /section /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree % missing /plusminus % missing /twosuperior % missing /threesuperior % missing /acute /mu % missing /paragraph /periodcentered /cedilla /onesuperior % missing /ordmasculine /guillemotright /onequarter % missing /onehalf % missing /threequarters % missing /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth % missing /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply % missing /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute % missing /Thorn % missing /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth % missing /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide % missing /oslash /ugrave /uacute /ucircumflex /udieresis /yacute % missing /thorn % missing /ydieresis ] def /NewFontDirectory FontDirectory maxlength dict def % % Apparently no guarantee findfont is defined in systemdict so the obvious % % systemdict /findfont get exec % % can generate an error. So far the only exception is a VT600 (version 48.0). % userdict /@RealFindfont known not { userdict begin /@RealFindfont systemdict begin /findfont load end def end } if /findfont { dup NewFontDirectory exch known not { dup %dup systemdict /findfont get exec % not always in systemdict dup userdict /@RealFindfont get exec dup /Encoding get StandardEncoding eq { dup length dict begin {1 index /FID ne {def}{pop pop} ifelse} forall /Encoding ISOLatin1Encoding def currentdict end /DummyFontName exch definefont } if NewFontDirectory 3 1 roll put } if NewFontDirectory exch get } bind def %%Patch from lp %%EndPatch from lp setup %%EndSetup %%Page: 1 1 /saveobj save def mark 1 pagesetup 12 /LucidaSans-Demi f (The) 1490 1230 w (Plan 9) 1761 1230 w (AML) 2177 1230 w (Interpreter and ACPI) 2483 1230 w (support) 3784 1230 w 10 /LucidaSans-Italic f (Francisco J. Ballesteros) 2319 1470 w (nemo@lsub.org) 2505 1590 w (ABSTRACT) 2626 2030 w 10 /LucidaSansUnicode00 f (This) 1330 2290 w (is) 1576 2290 w (a) 1697 2290 w (technical) 1793 2290 w (note) 2266 2290 w (regarding) 2523 2290 w (the) 3036 2290 w (implementation) 3233 2290 w (of) 4037 2290 w (the) 4177 2290 w (Plan) 4374 2290 w (9) 4617 2290 w (AML) 1080 2410 w (interpreter) 1330 2410 w (and) 1891 2410 w (support) 2113 2410 w (for) 2533 2410 w (ACPI.) 2714 2410 w (It) 3042 2410 w (is) 3150 2410 w (intended) 3271 2410 w (as) 3740 2410 w (an) 3887 2410 w (aid) 4045 2410 w (for) 4233 2410 w (those) 4413 2410 w (who must change or understand its implementation.) 1080 2530 w 10 /LucidaSans-Demi f (Introduction.) 720 2890 w 10 /LucidaSansUnicode00 f (ACPI) 970 3046 w (is) 1231 3046 w (an) 1350 3046 w (standard) 1506 3046 w (for) 1972 3046 w (configuration) 2151 3046 w (and) 2840 3046 w (power) 3060 3046 w (management) 3398 3046 w (on) 4069 3046 w (the) 4232 3046 w (PC.) 4427 3046 w (In) 4655 3046 w (ACPI,) 4786 3046 w (the) 720 3166 w (platform) 912 3166 w (\(motherboard) 1365 3166 w (and) 2068 3166 w (devices\)) 2285 3166 w (provides) 2713 3166 w (tables) 3166 3166 w (that) 3494 3166 w (convey) 3722 3166 w (configuration) 4092 3166 w (infor\255) 4777 3166 w (mation to the OS.) 720 3286 w (Besides) 970 3442 w (the) 1385 3442 w (static) 1591 3442 w (information) 1902 3442 w (found) 2520 3442 w (in) 2856 3442 w (tables,) 2998 3442 w (ACPI) 3372 3442 w (configuration) 3645 3442 w (includes) 4345 3442 w (byte\255) 4799 3442 w (code) 720 3562 w (for) 993 3562 w (a) 1174 3562 w (machine) 1271 3562 w (language) 1721 3562 w (known) 2206 3562 w (as) 2568 3562 w (AML.) 2716 3562 w (The) 2998 3562 w (OS) 3221 3562 w (is) 3395 3562 w (expected) 3517 3562 w (to) 4001 3562 w (execute) 4140 3562 w (AML) 4560 3562 w (code) 4809 3562 w (both to initialize ACPI and to trigger actions on devices \(e.g.,) 720 3682 w (to) 3708 3682 w (change) 3839 3682 w (the) 4220 3682 w (power) 4408 3682 w (state\).) 4739 3682 w (Also,) 720 3802 w (some) 1019 3802 w (hardware) 1337 3802 w (interrupts) 1844 3802 w (may) 2380 3802 w (require) 2637 3802 w (that) 3042 3802 w (we) 3290 3802 w (should) 3480 3802 w (call) 3864 3802 w (AML) 4084 3802 w (to) 4348 3802 w (handle) 4502 3802 w (the) 4885 3802 w (implied events.) 720 3922 w (AML) 970 4078 w (is) 1219 4078 w (the) 1340 4078 w (machine) 1536 4078 w (language) 1985 4078 w (for) 2469 4078 w (an) 2649 4078 w (abstract) 2807 4078 w (language) 3238 4078 w (known) 3722 4078 w (as) 4083 4078 w (ASL.) 4230 4078 w (It) 4479 4078 w (turns) 4586 4078 w (out) 4880 4078 w (that) 720 4198 w (AML) 950 4198 w (is) 1197 4198 w (more) 1316 4198 w (a) 1606 4198 w (representation) 1700 4198 w (of) 2446 4198 w (ASL) 2582 4198 w (than) 2796 4198 w (it) 3050 4198 w (is) 3154 4198 w (a) 3272 4198 w (language) 3365 4198 w (for) 3846 4198 w (a) 4023 4198 w (machine.) 4116 4198 w (In) 4594 4198 w (under\255) 4723 4198 w (standing) 720 4318 w (what) 1181 4318 w (the) 1452 4318 w (interpreter) 1647 4318 w (must) 2206 4318 w (do) 2489 4318 w (it) 2653 4318 w (helps) 2759 4318 w (to) 3060 4318 w (look) 3198 4318 w (at) 3448 4318 w (ASL) 3581 4318 w (definitions) 3798 4318 w (for) 4355 4318 w (the) 4535 4318 w (opera\255) 4731 4318 w (tions considered. AML is close to them.) 720 4438 w (The) 970 4594 w (language) 1191 4594 w (operates) 1674 4594 w (on) 2134 4594 w (objects) 2297 4594 w (that) 2686 4594 w (live) 2917 4594 w (in) 3123 4594 w (a) 3254 4594 w (tree-shaped) 3349 4594 w (namespace,) 3987 4594 w (global) 4602 4594 w (to) 4942 4594 w (AML.) 720 4714 w (The) 1039 4714 w (AML) 1267 4714 w 10 /LucidaSansUnicode20 f (\030\030) 1521 4714 w 10 /LucidaSansUnicode00 f (machine) 1585 4714 w 10 /LucidaSansUnicode20 f (\031\031) 1993 4714 w 10 /LucidaSansUnicode00 f (does) 2103 4714 w (not) 2380 4714 w (have) 2586 4714 w (a) 2857 4714 w (general) 2958 4714 w (purpose) 3365 4714 w (stack.) 3808 4714 w (It) 4138 4714 w (operates) 4250 4714 w (on) 4716 4714 w (the) 4885 4714 w (name) 720 4834 w (space) 1040 4834 w (and) 1370 4834 w (has) 1604 4834 w 10 /LucidaSansUnicode20 f (\030\030) 1826 4834 w 10 /LucidaSansUnicode00 f (builtin) 1890 4834 w (objects) 2256 4834 w 10 /LucidaSansUnicode20 f (\031\031) 2605 4834 w 10 /LucidaSansUnicode00 f (that) 2724 4834 w (represent) 2970 4834 w (arguments) 3488 4834 w (and) 4062 4834 w (local) 4297 4834 w (variables.) 4577 4834 w (Thus,) 720 4954 w (thanks) 1032 4954 w (to) 1399 4954 w (the) 1539 4954 w (AML) 1736 4954 w (specification,) 1985 4954 w (a) 2669 4954 w (name) 2765 4954 w (may) 3072 4954 w (represent) 3313 4954 w (a) 3817 4954 w (method) 3913 4954 w (object,) 4326 4954 w (or) 4697 4954 w (may) 4840 4954 w (not;) 720 5074 w (when) 948 5074 w (it) 1241 5074 w (does,) 1343 5074 w (what) 1642 5074 w (follows) 1909 5074 w (in) 2291 5074 w (the) 2419 5074 w (code) 2611 5074 w (may) 2879 5074 w (be) 3116 5074 w (arguments) 3272 5074 w (for) 3828 5074 w (it.) 4004 5074 w (There) 4139 5074 w (is) 4454 5074 w (no) 4571 5074 w (opera\255) 4731 5074 w (tion) 720 5194 w (for) 943 5194 w (calling) 1116 5194 w (a) 1466 5194 w (routine.) 1554 5194 w (In) 1967 5194 w (general,) 2091 5194 w (it) 2517 5194 w (is) 2616 5194 w (not) 2729 5194 w (feasible) 2922 5194 w (to) 3331 5194 w (say) 3462 5194 w (if) 3653 5194 w (a) 3752 5194 w (bytecode) 3840 5194 w (is) 4312 5194 w (an) 4425 5194 w (operation) 4575 5194 w (or raw data.) 720 5314 w (The) 970 5470 w (name) 1196 5470 w (space) 1507 5470 w (is) 1828 5470 w (initialized) 1953 5470 w (as) 2474 5470 w (a) 2626 5470 w (side-effect) 2727 5470 w (of) 3304 5470 w (loading) 3448 5470 w 10 /LucidaTypewriter f (DSDT) 3855 5470 w 10 /LucidaSansUnicode00 f (and) 4189 5470 w 10 /LucidaTypewriter f (SSDT) 4415 5470 w 10 /LucidaSansUnicode00 f (tables) 4749 5470 w (\(and evaluating AML code on them\).) 720 5590 w (Names) 970 5746 w (are) 1345 5746 w (four) 1543 5746 w (characters,) 1790 5746 w (but) 2368 5746 w (searching) 2576 5746 w (rules) 3091 5746 w (for) 3376 5746 w (names) 3561 5746 w (are) 3924 5746 w (not) 4123 5746 w (what) 4330 5746 w (could) 4608 5746 w (be) 4921 5746 w (expected.) 720 5866 w (For) 1249 5866 w (example,) 1459 5866 w (a) 1957 5866 w (relative) 2065 5866 w (name) 2473 5866 w (may) 2792 5866 w (be) 3045 5866 w (the) 3217 5866 w (resolved) 3425 5866 w (as) 3887 5866 w (the) 4046 5866 w (aunt) 4254 5866 w (of) 4523 5866 w (dot.) 4674 5866 w (As) 4920 5866 w (another) 720 5986 w (example,) 1146 5986 w (names) 1643 5986 w 10 /LucidaTypewriter f (_OS_) 2012 5986 w 10 /LucidaSansUnicode00 f (and) 2352 5986 w 10 /LucidaTypewriter f (_OS) 2584 5986 w 10 /LucidaSansUnicode00 f (are) 2852 5986 w (considered) 3056 5986 w (to) 3641 5986 w (be) 3791 5986 w (the) 3962 5986 w (same.) 4169 5986 w (The) 4508 5986 w (global) 4741 5986 w 10 /LucidaTypewriter f (acpins) 720 6106 w 10 /LucidaSansUnicode00 f (keeps the root of the name space.) 1184 6106 w (A) 970 6262 w (name) 1075 6262 w (refers) 1377 6262 w (to) 1695 6262 w (an) 1829 6262 w (object) 1983 6262 w (\(perhaps) 2318 6262 w (none\),) 2779 6262 w (and) 3122 6262 w (besides) 3339 6262 w (that,) 3745 6262 w (may) 4005 6262 w (or) 4242 6262 w (may) 4381 6262 w (not) 4618 6262 w (have) 4815 6262 w (children.) 720 6382 w (So,) 1186 6382 w (names) 1374 6382 w (are) 1732 6382 w (both) 1925 6382 w (like) 2189 6382 w (files) 2401 6382 w (and) 2643 6382 w (directories,) 2863 6382 w (depending) 3450 6382 w (on) 4006 6382 w (the) 4169 6382 w (point) 4364 6382 w (of) 4656 6382 w (view.) 4794 6382 w (This is an example \(portion\) of a name space:) 720 6502 w cleartomark showpage saveobj restore %%EndPage: 1 1 %%Page: 2 2 /saveobj save def mark 2 pagesetup 10 /LucidaSansUnicode00 f (\255 2 \255) 2783 480 w 9 /LucidaTypewriter f (/_SB_/PBTN=) 1635 830 w (dev) 2415 830 w () 2675 830 w (pnp) 3065 830 w ('PNP0C0C') 3325 830 w (addr) 3975 830 w (0x0) 4300 830 w ({) 4560 830 w (/_SB_/PBTN/_HID=) 1830 940 w (0xc0cd041) 3000 940 w (/_SB_/PBTN/_PRW=) 1830 1050 w (pkg[2]) 2935 1050 w ({) 3390 1050 w (0x8,) 3520 1050 w (0x4}) 3910 1050 w (/_SB_/PBTN/_PSW=) 1830 1160 w (method) 2935 1160 w (_PSW/1) 3390 1160 w (}) 1635 1270 w (/_SB_/_INI=) 1635 1380 w (method) 2415 1380 w (_INI/0) 2870 1380 w 10 /LucidaSansUnicode00 f (Here,) 720 1596 w 10 /LucidaTypewriter f (PBTN) 1026 1596 w 10 /LucidaSansUnicode00 f (is) 1361 1596 w (both) 1488 1596 w (a) 1758 1596 w (device) 1860 1596 w (and) 2214 1596 w (the) 2441 1596 w (root) 2643 1596 w (for) 2890 1596 w (a) 3076 1596 w (\(sub\)tree) 3178 1596 w (of) 3657 1596 w (ACPI) 3802 1596 w (objects) 4072 1596 w (referring) 4469 1596 w (to) 4942 1596 w 10 /LucidaTypewriter f (PBTN) 720 1716 w 10 /LucidaSansUnicode00 f (.) 1008 1716 w (Syntax) 1128 1716 w (for) 1505 1716 w (names) 1700 1716 w (in) 2073 1716 w (ACPI) 2220 1716 w (is) 2498 1716 w (actually) 2634 1716 w 10 /LucidaTypewriter f (\\_SB_.PBTN) 3060 1716 w 10 /LucidaSansUnicode00 f (and) 3836 1716 w (not) 4072 1716 w 10 /LucidaTypewriter f (/_SB_/PBTN) 4288 1716 w 10 /LucidaSansUnicode00 f (.) 5008 1716 w (This) 720 1836 w (implementation) 960 1836 w (uses) 1757 1836 w (the) 2013 1836 w (more) 2204 1836 w (familiar) 2491 1836 w (UNIX) 2895 1836 w (syntax) 3166 1836 w (\(with) 3520 1836 w (the) 3794 1836 w (required) 3985 1836 w (ACPI) 4432 1836 w (seman\255) 4690 1836 w (tics\).) 720 1956 w (There) 970 2112 w (are) 1292 2112 w (different) 1488 2112 w (object) 1950 2112 w (types) 2292 2112 w (including) 2595 2112 w (strings,) 3088 2112 w (buffers,) 3497 2112 w (integers,) 3920 2112 w (references) 4390 2112 w (to) 4942 2112 w (other) 720 2232 w (objects,) 1012 2232 w (buffer) 1428 2232 w (slices,) 1759 2232 w (register) 2092 2232 w (\(bit\)) 2499 2232 w (fields,) 2728 2232 w (memory) 3059 2232 w (\(or) 3489 2232 w (I/O\)) 3658 2232 w (regions,) 3885 2232 w (etc.) 4313 2232 w (An) 4555 2232 w (impor\255) 4720 2232 w (tant) 720 2352 w (object) 953 2352 w (type) 1293 2352 w (is) 1543 2352 w (called) 1665 2352 w (a) 1990 2352 w 10 /LucidaSans-Italic f (method) 2087 2352 w 10 /LucidaSansUnicode00 f (in) 2494 2352 w (AML.) 2628 2352 w (It) 2911 2352 w 10 /LucidaSansUnicode20 f (\031) 2977 2352 w 10 /LucidaSansUnicode00 f (s) 3009 2352 w (not) 3103 2352 w (a) 3306 2352 w 10 /LucidaSansUnicode20 f (\030\030) 3404 2352 w 10 /LucidaSansUnicode00 f (method) 3468 2352 w 10 /LucidaSansUnicode20 f (\031\031) 3840 2352 w 10 /LucidaSansUnicode00 f (,) 3904 2352 w (but) 3979 2352 w (a) 4184 2352 w (subroutine) 4282 2352 w (that) 4849 2352 w (may) 720 2472 w (be) 985 2472 w (called.) 1137 2472 w (In) 1517 2472 w (general, methods create objects in the name space and use them very) 1641 2472 w (much like local variables and, therefore, are not reentrant.) 720 2592 w (Execution) 970 2748 w (of) 1482 2748 w (AML) 1619 2748 w (operations) 1866 2748 w (is) 2421 2748 w (performed) 2540 2748 w (by) 3090 2748 w (using) 3245 2748 w (\(and) 3551 2748 w (modifying\)) 3804 2748 w (objects) 4365 2748 w (in) 4754 2748 w (the) 4885 2748 w (name) 720 2868 w (space.) 1040 2868 w (There) 1434 2868 w (are) 1766 2868 w (control) 1972 2868 w (operations,) 2368 2868 w (arithmetic) 2970 2868 w (operations,) 3514 2868 w (and) 4115 2868 w (operations) 4348 2868 w (on) 4917 2868 w (objects) 720 2988 w (\(roughly) 1116 2988 w (speaking\).) 1566 2988 w (There) 2115 2988 w (is) 2441 2988 w (no) 2569 2988 w (operation) 2740 2988 w (to) 3253 2988 w (call) 3399 2988 w (a) 3611 2988 w (method.) 3714 2988 w (AML) 4166 2988 w (is) 4422 2988 w (not) 4550 2988 w (a) 4758 2988 w (lan\255) 4861 2988 w (guage) 720 3108 w (for) 1072 3108 w (an) 1266 3108 w (abstract) 1438 3108 w (machine,) 1883 3108 w (but) 2377 3108 w (a) 2593 3108 w (compact) 2702 3108 w (representation) 3167 3108 w (of) 3928 3108 w (a) 4080 3108 w (source) 4189 3108 w (language.) 4565 3108 w (Thus,) 720 3228 w (evaluating) 1047 3228 w (AML) 1603 3228 w (is) 1868 3228 w (similar) 2005 3228 w (to) 2389 3228 w (interpreting) 2545 3228 w (source) 3178 3228 w (\(ASL\).) 3558 3228 w (For) 3890 3228 w (example,) 4104 3228 w (to) 4607 3228 w (call) 4763 3228 w (a) 4985 3228 w (method,) 720 3348 w (the) 1165 3348 w (name) 1361 3348 w (is) 1668 3348 w (placed) 1789 3348 w (into) 2147 3348 w (the) 2377 3348 w (bytecode) 2573 3348 w (and) 3053 3348 w (then) 3274 3348 w (the) 3531 3348 w (arguments.) 3726 3348 w (Depending) 4349 3348 w (on) 4917 3348 w (the context, a name found in the bytecode stream may imply a method call.) 720 3468 w (Most) 970 3624 w (operations) 1241 3624 w (copy) 1793 3624 w (objects) 2057 3624 w (and) 2443 3624 w (may) 2660 3624 w (lead) 2897 3624 w (to) 3137 3624 w (either) 3272 3624 w (explicit) 3590 3624 w (or) 3982 3624 w (implicit) 4121 3624 w (type) 4518 3624 w (casts,) 4763 3624 w (which) 720 3744 w (must) 1040 3744 w (follow) 1322 3744 w (rules) 1655 3744 w (indicated) 1933 3744 w (on) 2417 3744 w (the) 2579 3744 w (spec.) 2773 3744 w (Beware) 3065 3744 w (that) 3447 3744 w (casts) 3676 3744 w (are) 3959 3744 w (in) 4149 3744 w (many) 4278 3744 w (cases) 4578 3744 w (not) 4880 3744 w (what) 720 3864 w (could) 996 3864 w (be) 1307 3864 w (expected.) 1471 3864 w (For) 1991 3864 w (example,) 2192 3864 w (obtaining) 2683 3864 w (an) 3189 3864 w (integer) 3352 3864 w (from) 3741 3864 w (a) 4019 3864 w (string) 4120 3864 w (differs) 4448 3864 w (from) 4808 3864 w (what) 720 3984 w 10 /LucidaSans-Italic f (strtoul) 984 3984 w 10 /LucidaSansUnicode00 f (would) 1338 3984 w (do.) 1663 3984 w (Buffers) 1852 3984 w (may) 2227 3984 w (be) 2460 3984 w (handled) 2612 3984 w (as integers or buffers depending on their) 3035 3984 w (size.) 720 4104 w (Converting) 984 4104 w (a) 1554 4104 w (buffer) 1648 4104 w (to) 1983 4104 w (a) 2120 4104 w (string) 2215 4104 w (and) 2537 4104 w (writing) 2757 4104 w (it) 3134 4104 w (into) 3240 4104 w (a) 3469 4104 w (register) 3564 4104 w (does) 3977 4104 w (not) 4248 4104 w (produce) 4448 4104 w (the) 4885 4104 w (same effect than writing the buffer. Etcetera.) 720 4224 w (To look for further information on ACPI, ASL, and AML it helps to consider:) 970 4380 w 10 /LucidaSansUnicode22 f (\031) 720 4536 w 10 /LucidaSans-Italic f (Advanced Configuration and Power Interface Specification. 4.0. June 2009.) 970 4536 w 10 /LucidaSansUnicode00 f (This) 970 4656 w (is) 1210 4656 w (the) 1325 4656 w (ACPI) 1515 4656 w (specification.) 1772 4656 w (Chapter) 2450 4656 w (5) 2868 4656 w (is) 2966 4656 w (a) 3081 4656 w (description) 3171 4656 w (of) 3749 4656 w (the) 3882 4656 w (various) 4072 4656 w (ACPI) 4459 4656 w (tables,) 4717 4656 w (the) 970 4776 w (namespace,) 1161 4776 w (and) 1771 4776 w (important) 1987 4776 w (objects.) 2501 4776 w (Chapter) 2918 4776 w (18) 3337 4776 w (is) 3498 4776 w (a) 3613 4776 w (description) 3703 4776 w (of) 4281 4776 w (ASL.) 4414 4776 w (Chapter) 4657 4776 w (19) 970 4896 w (is) 1143 4896 w (a) 1270 4896 w (description) 1372 4896 w (of) 1962 4896 w (AML) 2108 4896 w (\(or,) 2364 4896 w (rather,) 2579 4896 w (a) 2951 4896 w (description) 3054 4896 w (of) 3645 4896 w (how) 3791 4896 w (ASL) 4039 4896 w (is) 4263 4896 w (encoded) 4391 4896 w (into) 4851 4896 w (AML\).) 970 5016 w (Section 18.2.5 lists the different objects and type conversion rules.) 1307 5016 w 10 /LucidaSansUnicode22 f (\031) 720 5172 w 10 /LucidaSans-Italic f (OpenBSD ACPI implementation.) 970 5172 w 10 /LucidaSansUnicode00 f (See) 970 5292 w 10 /LucidaTypewriter f (/sys/dev/acpi) 1180 5292 w 10 /LucidaSansUnicode00 f (in) 2160 5292 w (the) 2295 5292 w (system) 2494 5292 w (kernel) 2878 5292 w (source.) 3225 5292 w (Many) 3624 5292 w (operating) 3924 5292 w (systems) 4435 5292 w (use) 4871 5292 w (an) 970 5412 w (Intel) 1129 5412 w (provide) 1384 5412 w (implementation) 1791 5412 w (for) 2594 5412 w (the) 2774 5412 w (interpreter.) 2970 5412 w (However,) 3562 5412 w (this) 4052 5412 w (one) 4272 5412 w (is) 4492 5412 w (easier) 4613 5412 w (to) 4942 5412 w (understand and can be used as additional documentation for ACPI and AML.) 970 5532 w (The) 720 5688 w (AML) 934 5688 w (interpreter) 1175 5688 w (is) 1727 5688 w (currently) 1840 5688 w (work) 2304 5688 w (in) 2574 5688 w (progress,) 2698 5688 w (and) 3189 5688 w (can) 3402 5688 w (be) 3603 5688 w (used) 3755 5688 w (as) 4020 5688 w (a) 4159 5688 w (user) 4248 5688 w (program) 4492 5688 w (to) 4942 5688 w (parse) 720 5808 w (and) 1019 5808 w (execute) 1232 5808 w (AML) 1644 5808 w (code as dumped by a kernel during boot, using a temporary) 1885 5808 w 10 /LucidaSans-Italic f (acpi) 4839 5808 w 10 /LucidaSansUnicode00 f (device for such purpose.) 720 5928 w (When) 970 6084 w (ready,) 1287 6084 w (the) 1637 6084 w (interpreter) 1843 6084 w (must) 2413 6084 w (execute) 2707 6084 w (at) 3137 6084 w 10 /LucidaSans-Italic f (links) 3280 6084 w 10 /LucidaSansUnicode00 f (time) 3558 6084 w (during) 3825 6084 w (boot,) 4196 6084 w (or) 4502 6084 w (anytime) 4656 6084 w (before configuring devices. By that time, there are no processes in the kernel.) 720 6204 w 10 /LucidaSans-Demi f (Implementation) 720 6444 w 10 /LucidaSansUnicode00 f (The important files are:) 720 6600 w 10 /LucidaTypewriter f (aml.h) 720 6756 w 10 /LucidaSansUnicode00 f (It defines the data structures used by the interpreter.) 970 6876 w 10 /LucidaTypewriter f (aml.c) 720 7032 w 10 /LucidaSansUnicode00 f (A) 970 7152 w (driver) 1074 7152 w (for) 1391 7152 w (the) 1565 7152 w (interpreter.) 1755 7152 w (Most) 2341 7152 w (of) 2611 7152 w (it) 2744 7152 w (would) 2845 7152 w (go) 3172 7152 w (when) 3330 7152 w (in) 3622 7152 w (the) 3748 7152 w (kernel.) 3938 7152 w (It) 4339 7152 w (contains) 4441 7152 w (the) 4885 7152 w (main) 970 7272 w (program) 1249 7272 w (and) 1704 7272 w (code) 1923 7272 w (to) 2193 7272 w (translate) 2330 7272 w (the) 2792 7272 w (dumps) 2986 7272 w (made) 3357 7272 w (by) 3663 7272 w (the) 3817 7272 w (kernel) 4011 7272 w (to) 4352 7272 w (binary) 4489 7272 w (data) 4830 7272 w cleartomark showpage saveobj restore %%EndPage: 2 2 %%Page: 3 3 /saveobj save def mark 3 pagesetup 10 /LucidaSansUnicode00 f (\255 3 \255) 2783 480 w (that can be interpreted as AML bytecodes.) 970 840 w 10 /LucidaTypewriter f (amlrt.c) 720 996 w 10 /LucidaSansUnicode00 f (The) 970 1116 w (run) 1210 1116 w (time) 1434 1116 w (for) 1708 1116 w (the) 1907 1116 w (interpreter.) 2122 1116 w (Includes) 2733 1116 w (helpers) 3196 1116 w (and) 3614 1116 w (several) 3854 1116 w (important) 4254 1116 w (entry) 4792 1116 w (points to evaluate AML code.) 970 1236 w 10 /LucidaTypewriter f (amlop.c) 720 1392 w 10 /LucidaSansUnicode00 f (Implementation for AML operations.) 970 1512 w 10 /LucidaTypewriter f (amlconv.c) 720 1668 w 10 /LucidaSansUnicode00 f (Implementation) 970 1788 w (for) 1778 1788 w (type) 1964 1788 w (casts) 2219 1788 w (and) 2511 1788 w (object) 2738 1788 w (copying.) 3083 1788 w (This) 3542 1788 w (part) 3794 1788 w (of) 4037 1788 w (the) 4182 1788 w (standard) 4384 1788 w (is) 4858 1788 w (a) 4985 1788 w (tangled web and its code is kept appart.) 970 1908 w 10 /LucidaTypewriter f (kernel.c) 720 2064 w 10 /LucidaSansUnicode00 f (Compatibility tools to keep the interpreter code as ready for the kernel as feasible.) 970 2184 w 10 /LucidaTypewriter f (acpi.c) 720 2340 w 10 /LucidaSansUnicode00 f (Contains) 970 2460 w (tools) 1431 2460 w (that) 1705 2460 w (should) 1931 2460 w (be) 2294 2460 w (provided) 2448 2460 w (by) 2911 2460 w (the) 3061 2460 w (kernel) 3251 2460 w (ACPI) 3588 2460 w (driver.) 3845 2460 w (They) 4194 2460 w (are) 4462 2460 w (not) 4649 2460 w (part) 4844 2460 w (of) 970 2580 w (the) 1103 2580 w (interpreter) 1293 2580 w (but) 1847 2580 w (may) 2044 2580 w (be) 2279 2580 w (necessary) 2433 2580 w (for) 2943 2580 w (this) 3116 2580 w (program.) 3329 2580 w (There) 3811 2580 w (is) 4123 2580 w (a) 4237 2580 w (version) 4326 2580 w (for) 4712 2580 w (the) 4885 2580 w (kernel in) 970 2700 w 10 /LucidaTypewriter f (9pc/acpi.c) 1427 2700 w 10 /LucidaSansUnicode00 f (, intended to) 2147 2700 w (replace) 2802 2700 w (this) 3186 2700 w (file) 3398 2700 w (when) 3582 2700 w (the) 3872 2700 w (interpreter) 4060 2700 w (gets) 4612 2700 w (into) 4851 2700 w (the kernel.) 970 2820 w 10 /LucidaSans-Demi f (Names and objects) 720 3060 w 10 /LucidaSansUnicode00 f (The global) 720 3216 w 10 /LucidaTypewriter f (acpins) 1264 3216 w 10 /LucidaSansUnicode00 f (keeps the root of the name space. This is the data type for a name:) 1728 3216 w 9 /LucidaTypewriter f (struct) 920 3386 w (Aname) 1375 3386 w ({) 920 3496 w (char) 1440 3606 w (s[5];) 1960 3606 w (/*) 3000 3606 w (NNNN) 3195 3606 w (*/) 3585 3606 w (char*) 1440 3716 w (path;) 1960 3716 w (Aobj*) 1440 3826 w (o;) 1960 3826 w (Aname*) 1440 3936 w (parent;) 1960 3936 w (Aname*) 1440 4046 w (childhd;) 1960 4046 w (/*) 3000 4046 w (first) 3195 4046 w (child) 3585 4046 w (*/) 3975 4046 w (Aname*) 1440 4156 w (childtl;) 1960 4156 w (/*) 3000 4156 w (last) 3195 4156 w (child) 3520 4156 w (*/) 3910 4156 w (Aname*) 1440 4266 w (sibling;) 1960 4266 w (/*) 3000 4266 w (pointer) 3195 4266 w (to) 3715 4266 w (next) 3910 4266 w (sibling) 4235 4266 w (*/) 4755 4266 w (Atable*) 1440 4376 w (table;) 1960 4376 w (/*) 3000 4376 w ([ds]sdt) 3195 4376 w (table) 3715 4376 w (id;) 4105 4376 w (for) 4365 4376 w (unload) 4625 4376 w (*/) 5080 4376 w (Aname*) 1440 4486 w (mnext;) 1960 4486 w (/*) 3000 4486 w (in) 3195 4486 w (list) 3390 4486 w (of) 3715 4486 w (per-method) 3910 4486 w (binds) 4625 4486 w (*/) 5015 4486 w (};) 920 4596 w 10 /LucidaSansUnicode00 f (The) 720 4812 w (list) 943 4812 w (of) 1131 4812 w (children) 1271 4812 w (starts) 1706 4812 w (at) 2021 4812 w 10 /LucidaTypewriter f (childhd) 2156 4812 w 10 /LucidaSansUnicode00 f (.) 2660 4812 w (The) 2767 4812 w (actual) 2991 4812 w (name) 3323 4812 w (is) 3632 4812 w 10 /LucidaTypewriter f (s) 3755 4812 w 10 /LucidaSansUnicode00 f (,) 3827 4812 w (but) 3902 4812 w (the) 4107 4812 w (entire) 4305 4812 w 10 /LucidaTypewriter f (path) 4629 4812 w 10 /LucidaSansUnicode00 f (is) 4960 4812 w (included as a convenience. The object bound to this name, if any, is kept at) 720 4932 w 10 /LucidaTypewriter f (o) 4424 4932 w 10 /LucidaSansUnicode00 f (.) 4496 4932 w (The data type for an ACPI, or AML, object is this:) 970 5088 w 9 /LucidaTypewriter f (/*) 920 5258 w (*) 985 5368 w (ACPI) 1115 5368 w (Aobject.) 1440 5368 w (*/) 985 5478 w (struct) 920 5588 w (Aobj) 1375 5588 w ({) 920 5698 w (Ref;) 1440 5808 w (int) 1440 5918 w (type;) 1960 5918 w (/*) 4040 5918 w (for) 4235 5918 w (this) 4495 5918 w (object) 4820 5918 w (*/) 5275 5918 w (Aname*) 1440 6028 w (name;) 1960 6028 w (/*) 4040 6028 w (ACPI) 4235 6028 w (name) 4560 6028 w (if) 4885 6028 w (bound) 5080 6028 w (or) 5470 6028 w (nil) 5665 6028 w (*/) 5925 6028 w (Aobj*) 1440 6138 w (next;) 1960 6138 w (/*) 4040 6138 w (in) 4235 6138 w (list) 4430 6138 w (*/) 4755 6138 w cleartomark showpage saveobj restore %%EndPage: 3 3 %%Page: 4 4 /saveobj save def mark 4 pagesetup 10 /LucidaSansUnicode00 f (\255 4 \255) 2783 480 w 9 /LucidaTypewriter f (union{) 1440 830 w (u64int) 1960 940 w (ival;) 3000 940 w (/*) 4040 940 w (doubles) 4235 940 w (as) 4755 940 w (handle) 4950 940 w (*/) 5405 940 w (char*) 1960 1050 w (sval;) 3000 1050 w (/*) 4040 1050 w (strings) 4235 1050 w (and) 4755 1050 w (names) 5015 1050 w (*/) 5405 1050 w (Nbuf) 1960 1160 w (buf;) 3000 1160 w (/*) 4040 1160 w (byte) 4235 1160 w (buffer) 4560 1160 w (*/) 5015 1160 w (Nslice) 1960 1270 w (slice;) 3000 1270 w (/*) 4040 1270 w (buffer) 4235 1270 w (slice) 4690 1270 w (\(bits\)) 5080 1270 w (*/) 5535 1270 w (Nreg) 1960 1380 w (reg;) 3000 1380 w (/*) 4040 1380 w (memory,) 4235 1380 w (i/o,) 4755 1380 w (...) 5080 1380 w (region) 5340 1380 w (*/) 5795 1380 w (Nfield) 1960 1490 w (field;) 3000 1490 w (/*) 4040 1490 w (slice) 4235 1490 w (\(bits\)) 4625 1490 w (of) 5080 1490 w (object) 5275 1490 w (*/) 5730 1490 w (Nref) 1960 1600 w (oref;) 3000 1600 w (/*) 4040 1600 w (local,) 4235 1600 w (ref,) 4690 1600 w (arg) 5015 1600 w (*/) 5275 1600 w (Nlist) 1960 1710 w (list;) 3000 1710 w (/*) 4040 1710 w (packages) 4235 1710 w (*/) 4820 1710 w (Nscope) 1960 1820 w (scope;) 3000 1820 w (/*) 4040 1820 w (for) 4235 1820 w (parsing) 4495 1820 w (*/) 5015 1820 w (Nmethod) 1960 1930 w (method;) 3000 1930 w (Ndev) 1960 2040 w (dev;) 3000 2040 w (Npwr) 1960 2150 w (pwr;) 3000 2150 w (Nproc) 1960 2260 w (proc;) 3000 2260 w (Nthermal) 1960 2370 w (thermal;) 3000 2370 w (Nmutex) 1960 2480 w (mutex;) 3000 2480 w (Nevent) 1960 2590 w (event;) 3000 2590 w (};) 1440 2700 w (};) 920 2810 w 10 /LucidaSansUnicode00 f (Objects) 720 3026 w (may) 1122 3026 w (be) 1358 3026 w (kept) 1513 3026 w (in) 1763 3026 w (a) 1891 3026 w (list) 1983 3026 w (of) 2166 3026 w (free) 2301 3026 w (objects) 2528 3026 w (\(when) 2914 3026 w (free\)) 3241 3026 w (or) 3501 3026 w (in) 3640 3026 w (a) 3768 3026 w (list) 3860 3026 w (of) 4043 3026 w (objects) 4178 3026 w (contained) 4564 3026 w (in) 720 3146 w (a) 847 3146 w 10 /LucidaSans-Italic f (package) 938 3146 w 10 /LucidaSansUnicode00 f (object) 1379 3146 w (\(an) 1713 3146 w (ACPI) 1899 3146 w (array\).) 2157 3146 w (They) 2502 3146 w (are) 2770 3146 w (reference) 2957 3146 w (counted) 3448 3146 w (\(including) 3875 3146 w (as) 4392 3146 w (references) 4533 3146 w (those from names, from other objects, from the interpreter stack, etc.\).) 720 3266 w 10 /LucidaTypewriter f (Type) 4253 3266 w 10 /LucidaSansUnicode00 f (may be:) 4573 3266 w 9 /LucidaTypewriter f (Onone) 920 3436 w (=) 1440 3436 w (0,) 1635 3436 w (Ofree,) 920 3546 w (/*) 2480 3546 w (debug) 2675 3546 w (*/) 3065 3546 w (Oslice,) 920 3656 w (/*) 2480 3656 w (buffer) 2675 3656 w (field) 3130 3656 w (or) 3520 3656 w (slice) 3715 3656 w (*/) 4105 3656 w (Oscope,) 920 3766 w (/*) 2480 3766 w (scope) 2675 3766 w (*/) 3065 3766 w (Omethod,) 920 3876 w (/*) 2480 3876 w (method) 2675 3876 w (*/) 3130 3876 w (Odev,) 920 3986 w (/*) 2480 3986 w (device) 2675 3986 w (*/) 3130 3986 w (Oproc,) 920 4096 w (/*) 2480 4096 w (processor) 2675 4096 w (*/) 3325 4096 w (Opwr,) 920 4206 w (/*) 2480 4206 w (pwrrsrc) 2675 4206 w (*/) 3195 4206 w (Othermal,) 920 4316 w (/*) 2480 4316 w (thermal) 2675 4316 w (zone) 3195 4316 w (*/) 3520 4316 w (Oref,) 920 4426 w (/*) 2480 4426 w (ref) 2675 4426 w (to) 2935 4426 w (object) 3130 4426 w (*/) 3585 4426 w (Olocal,) 920 4536 w (/*) 2480 4536 w (ref) 2675 4536 w (to) 2935 4536 w (local) 3130 4536 w (object) 3520 4536 w (*/) 3975 4536 w (Oarg,) 920 4646 w (/*) 2480 4646 w (ref) 2675 4646 w (to) 2935 4646 w (arg) 3130 4646 w (object) 3390 4646 w (*/) 3845 4646 w (Ohandle,) 920 4756 w (/*) 2480 4756 w (definition) 2675 4756 w (block) 3390 4756 w (handle) 3780 4756 w (*/) 4235 4756 w (Obuf) 920 4866 w (=) 1440 4866 w ('B',) 1570 4866 w (/*) 2480 4866 w (buf) 2675 4866 w (*/) 2935 4866 w (Oevent) 920 4976 w (=) 1440 4976 w ('E',) 1570 4976 w (/*) 2480 4976 w (sleep/wakeup) 2675 4976 w (*/) 3520 4976 w (Ofield) 920 5086 w (=) 1440 5086 w ('F',,) 1570 5086 w (/*) 2480 5086 w (register) 2675 5086 w (field) 3260 5086 w (*/) 3650 5086 w (Oint) 920 5196 w (=) 1440 5196 w ('I',) 1570 5196 w (/*) 2480 5196 w (integer) 2675 5196 w (*/) 3195 5196 w (Oname) 920 5306 w (=) 1440 5306 w ('N',) 1570 5306 w (/*) 2480 5306 w (a) 2675 5306 w (name) 2805 5306 w (*/) 3130 5306 w (Omutex) 920 5416 w (=) 1440 5416 w ('M',) 1570 5416 w (/*) 2480 5416 w (mutex) 2675 5416 w (*/) 3065 5416 w (Opkg) 920 5526 w (=) 1440 5526 w ('P',) 1570 5526 w (/*) 2480 5526 w (package) 2675 5526 w (object) 3195 5526 w (\(array\)) 3650 5526 w (*/) 4170 5526 w (Oreg) 920 5636 w (=) 1440 5636 w ('R',) 1570 5636 w (/*) 2480 5636 w (data) 2675 5636 w (region) 3000 5636 w (*/) 3455 5636 w (Ostr) 920 5746 w (=) 1440 5746 w ('S',) 1570 5746 w (/*) 2480 5746 w (string) 2675 5746 w (*/) 3130 5746 w (Odecstr) 920 5856 w (=) 1440 5856 w ('D',) 1570 5856 w (/*) 2480 5856 w (like) 2675 5856 w (string,) 3000 5856 w (but) 3520 5856 w (bytes) 3780 5856 w (from) 4170 5856 w (buffer) 4495 5856 w (*/) 4950 5856 w (/*) 2480 5966 w (values) 2675 5966 w (are) 3130 5966 w (printed) 3390 5966 w (in) 3910 5966 w (decimal) 4105 5966 w (*/) 4625 5966 w (Oany) 920 6076 w (=) 1440 6076 w ('O',) 1570 6076 w (/*) 2480 6076 w (any) 2675 6076 w (object) 2935 6076 w (after) 3390 6076 w (resolving) 3780 6076 w (names) 4430 6076 w (*/) 4820 6076 w 10 /LucidaTypewriter f (Onone) 720 6292 w 10 /LucidaSansUnicode00 f (is) 1124 6292 w (uninitialized.) 1248 6292 w 10 /LucidaTypewriter f (Ofree) 1955 6292 w 10 /LucidaSansUnicode00 f (is) 2359 6292 w (used) 2483 6292 w (while) 2759 6292 w (the) 3056 6292 w (object) 3255 6292 w (is) 3598 6292 w (on) 3723 6292 w (the) 3891 6292 w (free) 4091 6292 w (list) 4326 6292 w (\(for) 4517 6292 w (debug) 4734 6292 w (checks\).) 720 6412 w 10 /LucidaTypewriter f (Oslice) 1180 6412 w 10 /LucidaSansUnicode00 f (is) 1646 6412 w (called) 1760 6412 w (a) 2077 6412 w 10 /LucidaSans-Italic f (field) 2166 6412 w 10 /LucidaSansUnicode00 f (in) 2410 6412 w (ACPI,) 2534 6412 w (but) 2821 6412 w (there) 3016 6412 w (are) 3301 6412 w (three) 3486 6412 w (or) 3771 6412 w (four) 3906 6412 w (types) 4140 6412 w (of) 4432 6412 w (fields) 4563 6412 w (it) 4861 6412 w (is) 4960 6412 w (easier) 720 6532 w (to) 1052 6532 w (understand) 1194 6532 w (this) 1790 6532 w (object) 2013 6532 w (as) 2355 6532 w (a) 2505 6532 w (slice) 2604 6532 w (of) 2864 6532 w (a) 3006 6532 w (buffer.) 3105 6532 w (We) 3477 6532 w (use) 3664 6532 w 10 /LucidaTypewriter f (Ofield) 3878 6532 w 10 /LucidaSansUnicode00 f (for) 4355 6532 w (fields) 4539 6532 w (that) 4849 6532 w (identify) 720 6652 w (bit-slices) 1118 6652 w (of) 1605 6652 w (other) 1736 6652 w (fields) 2026 6652 w (or) 2324 6652 w (system) 2459 6652 w (memory) 2832 6652 w (and) 3261 6652 w (I/O spaces.) 3474 6652 w 10 /LucidaTypewriter f (Opkg) 4089 6652 w 10 /LucidaSansUnicode00 f (is actually an) 4409 6652 w (array.) 720 6772 w (A slice value \(buffer field in ACPI\) is defined as) 970 6928 w cleartomark showpage saveobj restore %%EndPage: 4 4 %%Page: 5 5 /saveobj save def mark 5 pagesetup 10 /LucidaSansUnicode00 f (\255 5 \255) 2783 480 w 9 /LucidaTypewriter f (/*) 920 830 w (*) 985 940 w (buffer) 1115 940 w (field.) 1570 940 w (slice) 2025 940 w (of) 2415 940 w (bits) 2610 940 w (in) 2935 940 w (buffer.) 3130 940 w (*/) 985 1050 w (struct) 920 1160 w (Nslice) 1375 1160 w ({) 920 1270 w (Aobj*) 1440 1380 w (src;) 1960 1380 w (/*) 3000 1380 w (source) 3195 1380 w (Obuf) 3650 1380 w (object) 3975 1380 w (*/) 4430 1380 w (uintptr) 1440 1490 w (off;) 1960 1490 w (/*) 3000 1490 w (starting) 3195 1490 w (bit) 3780 1490 w (index) 4040 1490 w (*/) 4430 1490 w (uintptr) 1440 1600 w (len;) 1960 1600 w (/*) 3000 1600 w (number) 3195 1600 w (of) 3650 1600 w (bits) 3845 1600 w (*/) 4170 1600 w (};) 920 1710 w 10 /LucidaSansUnicode00 f (We) 720 1926 w (keep) 908 1926 w (offset) 1187 1926 w (and) 1512 1926 w (length) 1738 1926 w (in) 2092 1926 w (bits) 2229 1926 w (because) 2455 1926 w 10 /LucidaTypewriter f (Nslice) 2895 1926 w 10 /LucidaSansUnicode00 f (is) 3373 1926 w (used) 3499 1926 w (to) 3777 1926 w (represent) 3921 1926 w (several) 4431 1926 w (ACPI) 4818 1926 w (field) 720 2046 w (types) 990 2046 w (with) 1305 2046 w (bit/byte/...) 1566 2046 w (granularity.) 2160 2046 w (A) 2804 2046 w (general) 2928 2046 w (purpose) 3344 2046 w 10 /LucidaTypewriter f (bitcpy) 3796 2046 w 10 /LucidaSansUnicode00 f (function) 4283 2046 w (in) 4739 2046 w (the) 4885 2046 w (interpreter is used to move bytes when feasible and bits otherwise.) 720 2166 w (An address space region \(memory, I/O, PCI, etc.\) is represented by) 970 2322 w 10 /LucidaTypewriter f (Nreg) 4244 2322 w 10 /LucidaSansUnicode00 f (:) 4532 2322 w 9 /LucidaTypewriter f (struct) 920 2492 w (Nreg) 1375 2492 w ({) 920 2602 w (int) 1440 2712 w (spc;) 1960 2712 w (/*) 3000 2712 w (io) 3195 2712 w (space) 3390 2712 w (type) 3780 2712 w (*/) 4105 2712 w (u64int) 1440 2822 w (base;) 1960 2822 w (/*) 3000 2822 w (address,) 3195 2822 w (physical) 3780 2822 w (*/) 4365 2822 w (uchar*) 1440 2932 w (p;) 1960 2932 w (/*) 3000 2932 w (address,) 3195 2932 w (kmapped) 3780 2932 w (*/) 4300 2932 w (uintptr) 1440 3042 w (len;) 1960 3042 w (uint) 1440 3152 w (tbdf;) 1960 3152 w (/*) 3000 3152 w (pci) 3195 3152 w (only) 3455 3152 w (*/) 3780 3152 w (};) 920 3262 w 10 /LucidaSansUnicode00 f (All) 720 3478 w (other) 887 3478 w (field) 1184 3478 w (types) 1438 3478 w (in) 1737 3478 w (ACPI) 1868 3478 w (are) 2131 3478 w (represented) 2324 3478 w (by) 2947 3478 w 10 /LucidaTypewriter f (Nfield) 3103 3478 w 10 /LucidaSansUnicode00 f (.) 3535 3478 w (This) 3640 3478 w (represents) 3886 3478 w (a) 4441 3478 w (slice) 4537 3478 w (\(bits\)) 4794 3478 w (of a region or another field and is the main object used to perform I/O.) 720 3598 w 9 /LucidaTypewriter f (/*) 920 3768 w (*) 985 3878 w (bit) 1115 3878 w (field) 1375 3878 w (for) 1765 3878 w (a) 2025 3878 w (region,) 2155 3878 w (or) 2675 3878 w (banked-register) 2870 3878 w (bit) 3910 3878 w (field,) 4170 3878 w (or) 4625 3878 w (*) 985 3988 w (\(index,data\)) 1115 3988 w (register) 1960 3988 w (bit) 2545 3988 w (field.) 2805 3988 w (*/) 985 4098 w (struct) 920 4208 w (Nfield) 1375 4208 w ({) 920 4318 w (char*) 1440 4428 w (fname;) 1960 4428 w (/*) 3000 4428 w (field) 3195 4428 w (name) 3585 4428 w (*/) 3910 4428 w (int) 1440 4538 w (accsz;) 1960 4538 w (/*) 3000 4538 w (access) 3195 4538 w (sz) 3650 4538 w (in) 3845 4538 w (bits) 4040 4538 w (for) 4365 4538 w (source*/) 4625 4538 w (int) 1440 4648 w (locking;) 1960 4648 w (/*) 3000 4648 w (needs) 3195 4648 w (locking) 3585 4648 w (*/) 4105 4648 w (int) 1440 4758 w (update;) 1960 4758 w (/*) 3000 4758 w (how) 3195 4758 w (to) 3455 4758 w (update) 3650 4758 w (*/) 4105 4758 w (uintptr) 1440 4868 w (off;) 1960 4868 w (/*) 3000 4868 w (in) 3195 4868 w (bits) 3390 4868 w (*/) 3715 4868 w (int) 1440 4978 w (len;) 1960 4978 w (/*) 3000 4978 w (in) 3195 4978 w (bits) 3390 4978 w (*/) 3715 4978 w (Aobj*) 1440 5088 w (reg;) 1960 5088 w (/*) 3000 5088 w (region) 3195 5088 w (the) 3650 5088 w (field) 3910 5088 w (is) 4300 5088 w (for) 4495 5088 w (*/) 4755 5088 w (Aobj*) 1440 5198 w (bank;) 1960 5198 w (/*) 3000 5198 w (bank) 3195 5198 w (name) 3520 5198 w (\(for) 3845 5198 w (banked) 4170 5198 w (field\)) 4625 5198 w (or) 5080 5198 w (nil) 5275 5198 w (*/) 5535 5198 w (Aobj*) 1440 5308 w (bankval;) 1960 5308 w (/*) 3000 5308 w (bank) 3195 5308 w (value) 3520 5308 w (\(for) 3910 5308 w (banked) 4235 5308 w (field\)) 4690 5308 w (*/) 5145 5308 w (Aobj*) 1440 5418 w (idx;) 1960 5418 w (/*) 3000 5418 w (index) 3195 5418 w (reg.) 3585 5418 w (\(for) 3910 5418 w (index) 4235 5418 w (field\)) 4625 5418 w (or) 5080 5418 w (nil) 5275 5418 w (*/) 5535 5418 w (Aobj*) 1440 5528 w (idxval;) 1960 5528 w (/*) 3000 5528 w (value) 3195 5528 w (for) 3585 5528 w (index) 3845 5528 w (*/) 4235 5528 w (Aobj*) 1440 5638 w (data;) 1960 5638 w (/*) 3000 5638 w (data) 3195 5638 w (\(for) 3520 5638 w (index) 3845 5638 w (field\)) 4235 5638 w (or) 4690 5638 w (nil) 4885 5638 w (*/) 5145 5638 w (};) 920 5748 w 10 /LucidaSansUnicode00 f (To) 720 5964 w (make) 877 5964 w (things) 1172 5964 w (uniform,) 1508 5964 w (it) 1958 5964 w (has) 2057 5964 w (bit) 2258 5964 w (granularity) 2420 5964 w (so) 2978 5964 w (we) 3123 5964 w (may) 3290 5964 w (forget) 3524 5964 w (in) 3852 5964 w (general) 3977 5964 w (if) 4372 5964 w (it) 4472 5964 w (is) 4572 5964 w (a) 4686 5964 w (bit) 4775 5964 w (or) 4938 5964 w (byte) 720 6084 w (aligned) 961 6084 w (field.) 1350 6084 w 10 /LucidaTypewriter f (Accsz) 1661 6084 w 10 /LucidaSansUnicode00 f (dictates) 2054 6084 w (the number of bits to I/O at a time.) 2466 6084 w 10 /LucidaTypewriter f (Update) 4248 6084 w 10 /LucidaSansUnicode00 f (dictate) 4712 6084 w (what) 720 6204 w (to) 995 6204 w (do) 1137 6204 w (with) 1305 6204 w (remaining) 1554 6204 w (bits) 2088 6204 w (in) 2313 6204 w (the) 2449 6204 w (source) 2649 6204 w (object) 3016 6204 w (upon) 3359 6204 w (writes) 3652 6204 w (to) 3988 6204 w (the) 4131 6204 w (field) 4331 6204 w (\(preverve) 4590 6204 w (them) 720 6324 w (by) 1017 6324 w (reading) 1181 6324 w (them) 1598 6324 w (before,) 1895 6324 w (write) 2290 6324 w (as) 2579 6324 w (one) 2734 6324 w (or) 2962 6324 w (write) 3112 6324 w (as) 3400 6324 w (zero\).) 3554 6324 w (There) 3882 6324 w (are) 4208 6324 w (two) 4408 6324 w (kinds) 4631 6324 w (of) 4942 6324 w (fields:) 720 6444 w 10 /LucidaSans-Italic f (Index fields) 720 6600 w 10 /LucidaSansUnicode00 f (A slice of a) 970 6720 w 10 /LucidaTypewriter f (data) 1536 6720 w 10 /LucidaSansUnicode00 f (register available after setting an index register,) 1856 6720 w 10 /LucidaTypewriter f (idx) 4229 6720 w 10 /LucidaSansUnicode00 f (, to a value.) 4445 6720 w 10 /LucidaSans-Italic f (Region fields) 720 6876 w 10 /LucidaSansUnicode00 f (A slice of a region, perhaps requiring a write to a) 970 6996 w 10 /LucidaTypewriter f (bank) 3387 6996 w 10 /LucidaSansUnicode00 f (register before using.) 3707 6996 w cleartomark showpage saveobj restore %%EndPage: 5 5 %%Page: 6 6 /saveobj save def mark 6 pagesetup 10 /LucidaSansUnicode00 f (\255 6 \255) 2783 480 w (Scope) 970 840 w (objects) 1291 840 w (are) 1676 840 w (not) 1864 840 w (ACPI) 2060 840 w (objects.) 2319 840 w (They) 2737 840 w (are) 3007 840 w (used) 3196 840 w (to) 3465 840 w (represent) 3600 840 w (a) 4100 840 w (scope) 4192 840 w (\(also) 4511 840 w (intro\255) 4777 840 w (duced by an AML) 720 960 w 10 /LucidaSans-Italic f (scope) 1583 960 w 10 /LucidaSansUnicode00 f (operation\) as an aid in parsing.) 1885 960 w 9 /LucidaTypewriter f (struct) 920 1130 w (Nscope) 1375 1130 w ({) 920 1240 w (char*) 1440 1350 w (name;) 1960 1350 w (uchar*) 1440 1460 w (pe;) 1960 1460 w (/*) 3000 1460 w (end) 3195 1460 w (of) 3455 1460 w (AML) 3650 1460 w (for) 3910 1460 w (it) 4170 1460 w (*/) 4365 1460 w (};) 920 1570 w 10 /LucidaSansUnicode00 f (A) 720 1786 w (method) 822 1786 w (including) 1228 1786 w (information) 1711 1786 w (about) 2312 1786 w (the) 2624 1786 w (number) 2813 1786 w (of) 3224 1786 w (arguments) 3356 1786 w (and) 3909 1786 w (the) 4123 1786 w (portion) 4312 1786 w (of) 4700 1786 w (AML) 4832 1786 w (implementing the method) 720 1906 w 10 /LucidaSansUnicode20 f (\031) 1982 1906 w 10 /LucidaSansUnicode00 f (s body.) 2014 1906 w 9 /LucidaTypewriter f (struct) 920 2076 w (Nmethod) 1375 2076 w ({) 920 2186 w (char*) 1440 2296 w (name;) 1960 2296 w (int) 1440 2406 w (nargs;) 1960 2406 w (/*) 3000 2406 w (arity) 3195 2406 w (*/) 3585 2406 w (int) 1440 2516 w (isexcl;) 1960 2516 w (/*) 3000 2516 w (is) 3195 2516 w (serialized?) 3390 2516 w (*/) 4170 2516 w (int) 1440 2626 w (synclvl;) 1960 2626 w (int) 1440 2736 w (trace;) 1960 2736 w (/*) 3000 2736 w (1:) 3195 2736 w (on;) 3390 2736 w (-1:) 3650 2736 w (off;) 3910 2736 w (0:) 4235 2736 w (as) 4430 2736 w (you) 4625 2736 w (were) 4885 2736 w (*/) 5210 2736 w (uchar*) 1440 2846 w (ps;) 1960 2846 w (/*) 3000 2846 w (aml) 3195 2846 w (for) 3455 2846 w (this) 3715 2846 w (method) 4040 2846 w (*/) 4495 2846 w (uchar*) 1440 2956 w (pe;) 1960 2956 w (/*) 3000 2956 w (end) 3195 2956 w (of) 3455 2956 w (aml) 3650 2956 w (*/) 3910 2956 w (void) 1440 3066 w (\(*f\)\(Aobj*\);) 1960 3066 w (/*) 3000 3066 w (C) 3195 3066 w (implementation) 3325 3066 w (for) 4300 3066 w (builtins) 4560 3066 w (*/) 5145 3066 w (};) 920 3176 w 10 /LucidaSansUnicode00 f (There are other objects that should be either self-explanatory or uninteresting by now.) 720 3392 w 10 /LucidaSans-Demi f (Devices) 720 3632 w 10 /LucidaSansUnicode00 f (Devices) 720 3788 w (include) 1135 3788 w (information) 1532 3788 w (about) 2144 3788 w (resources) 2467 3788 w (for) 2982 3788 w (them.) 3166 3788 w (They) 3491 3788 w (are) 3769 3788 w (kept) 3966 3788 w (in) 4225 3788 w (al) 4361 3788 w (list) 4490 3788 w (of) 4681 3788 w 10 /LucidaTypewriter f (Res) 4824 3788 w 10 /LucidaSansUnicode00 f (items.) 720 3908 w 9 /LucidaTypewriter f (/*) 920 4078 w (*) 985 4188 w (ACPI) 1115 4188 w (device) 1440 4188 w (as) 1895 4188 w (seen) 2090 4188 w (by) 2415 4188 w (drivers.) 2610 4188 w (*/) 985 4298 w (struct) 920 4408 w (ACPIdev) 1375 4408 w ({) 920 4518 w (char*) 1440 4628 w (name;) 1960 4628 w (char*) 1440 4738 w (pnpid;) 1960 4738 w (/*) 3000 4738 w (pnp) 3195 4738 w (id) 3455 4738 w (*/) 3650 4738 w (u64int) 1440 4848 w (addr;) 1960 4848 w (/*) 3000 4848 w (address) 3195 4848 w (*/) 3715 4848 w (Res*) 1440 4958 w (res;) 1960 4958 w (/*) 3000 4958 w (current) 3195 4958 w (resource) 3715 4958 w (settings) 4300 4958 w (*/) 4885 4958 w (};) 920 5068 w (struct) 920 5288 w (Ndev) 1375 5288 w ({) 920 5398 w (ACPIdev;) 1440 5508 w (uchar*) 1440 5618 w (pe;) 1960 5618 w (void) 1440 5728 w (\(*handler\)\(Aobj*,) 1960 5728 w (int\);) 3130 5728 w (/*) 3520 5728 w (for) 3715 5728 w (events) 3975 5728 w (*/) 4430 5728 w (void*) 1440 5838 w (aux;) 1960 5838 w (};) 920 5948 w 10 /LucidaSansUnicode00 f (A resource is described by this data type:) 720 6164 w cleartomark showpage saveobj restore %%EndPage: 6 6 %%Page: 7 7 /saveobj save def mark 7 pagesetup 10 /LucidaSansUnicode00 f (\255 7 \255) 2783 480 w 9 /LucidaTypewriter f (struct) 920 830 w (Res) 1375 830 w ({) 920 940 w (Res*) 1440 1050 w (next;) 1960 1050 w (/*) 3000 1050 w (in) 3195 1050 w (resource) 3390 1050 w (list) 3975 1050 w (*/) 4300 1050 w (int) 1440 1160 w (type;) 1960 1160 w (/*) 3000 1160 w (resource) 3195 1160 w (type) 3780 1160 w (*/) 4105 1160 w (union{) 1440 1270 w (struct{) 1960 1380 w (u32int) 2480 1490 w (irqs[16];) 3000 1490 w (uint) 2480 1600 w (flags;) 3000 1600 w (int) 2480 1710 w (idx;) 3000 1710 w (/*) 3520 1710 w (index) 3715 1710 w (in) 4105 1710 w (src) 4300 1710 w (*/) 4560 1710 w (char*) 2480 1820 w (src;) 3000 1820 w (/*) 3520 1820 w (where) 3715 1820 w (resources) 4105 1820 w (come) 4755 1820 w (from) 5080 1820 w (*/) 5405 1820 w (}irq;) 1960 1930 w (struct{) 1960 2040 w (uint) 2480 2150 w (chans;) 3000 2150 w (uint) 2480 2260 w (flags;) 3000 2260 w (}dma;) 1960 2370 w (struct{) 1960 2600 w (u64int) 2480 2710 w (min;) 3000 2710 w (/*) 3520 2710 w (address) 3715 2710 w (of) 4235 2710 w (the) 4430 2710 w (range) 4690 2710 w (*/) 5080 2710 w (u64int) 2480 2820 w (max;) 3000 2820 w (/*) 3520 2820 w (address) 3715 2820 w (of) 4235 2820 w (the) 4430 2820 w (range) 4690 2820 w (*/) 5080 2820 w (uint) 2480 2930 w (align;) 3000 2930 w (int) 2480 3040 w (isrw;) 3000 3040 w (/*) 3520 3040 w (memory) 3715 3040 w (only,) 4170 3040 w (ro) 4560 3040 w (or) 4755 3040 w (rw?) 4950 3040 w (*/) 5210 3040 w (}io,) 1960 3150 w (mem;) 2285 3150 w (struct{) 1960 3380 w (u64int) 2480 3490 w (mask;) 3000 3490 w (/*) 3520 3490 w (which) 3715 3490 w (bits) 4105 3490 w (are) 4430 3490 w (decoded) 4690 3490 w (*/) 5210 3490 w (u64int) 2480 3600 w (min;) 3000 3600 w (/*) 3520 3600 w (address) 3715 3600 w (on) 4235 3600 w (the) 4430 3600 w (other) 4690 3600 w (side) 5080 3600 w (*/) 5405 3600 w (u64int) 2480 3710 w (max;) 3000 3710 w (u64int) 2480 3820 w (off;) 3000 3820 w (/*) 3520 3820 w (translation) 3715 3820 w (adds) 4495 3820 w (this) 4820 3820 w (*/) 5145 3820 w (int) 2480 3930 w (idx;) 3000 3930 w (/*) 3520 3930 w (index) 3715 3930 w (in) 4105 3930 w (src) 4300 3930 w (*/) 4560 3930 w (char*) 2480 4040 w (src;) 3000 4040 w (/*) 3520 4040 w (where) 3715 4040 w (resources) 4105 4040 w (come) 4755 4040 w (from) 5080 4040 w (*/) 5405 4040 w (int) 2480 4150 w (flags;) 3000 4150 w (int) 2480 4260 w (rev;) 3000 4260 w (/*) 3520 4260 w (acpi) 3715 4260 w (revision) 4040 4260 w (id) 4625 4260 w (or) 4820 4260 w (0) 5015 4260 w (*/) 5145 4260 w (u64int) 2480 4370 w (attr;) 3000 4370 w (}as;) 1960 4480 w (Gas) 1960 4590 w (field;) 2220 4590 w (/*) 1960 4820 w (non) 2155 4820 w (ACPI) 2415 4820 w (resources;) 2740 4820 w (invented) 3455 4820 w (by) 4040 4820 w (us) 4235 4820 w (to) 4430 4820 w (keep) 4625 4820 w (them) 4950 4820 w (here) 5275 4820 w (*/) 5600 4820 w (struct{) 1960 4930 w (u64int) 2480 5040 w (addr;) 3000 5040 w (int) 2480 5150 w (pin;) 3000 5150 w (char*) 2480 5260 w (src;) 3000 5260 w (int) 2480 5370 w (idx;) 3000 5370 w (}prt;) 1960 5480 w (/*) 3520 5480 w (pci) 3715 5480 w (intr.) 3975 5480 w (routing,) 4365 5480 w (from) 4950 5480 w (_PRT) 5275 5480 w (*/) 5600 5480 w (};) 1440 5590 w (};) 920 5700 w 10 /LucidaSansUnicode00 f (This) 720 5916 w (is) 973 5916 w (a) 1101 5916 w 10 /LucidaSansUnicode20 f (\030\030) 1204 5916 w 10 /LucidaSansUnicode00 f (compact) 1268 5916 w 10 /LucidaSansUnicode20 f (\031\031) 1679 5916 w 10 /LucidaSansUnicode00 f (representation) 1792 5916 w (of) 2548 5916 w (the) 2695 5916 w (ACPI) 2899 5916 w (data) 3170 5916 w (structures) 3429 5916 w (for) 3967 5916 w (resource) 4155 5916 w (settings,) 4623 5916 w (parsed) 720 6036 w (by) 1103 6036 w (the) 1272 6036 w (implementation.) 1481 6036 w (A) 2329 6036 w (cleaner) 2452 6036 w (structure) 2856 6036 w (should) 3348 6036 w (be) 3729 6036 w (used) 3901 6036 w (instead,) 4186 6036 w (once) 4624 6036 w (we) 4907 6036 w (know which pieces of information are really necessary.) 720 6156 w (There) 970 6312 w (are) 1290 6312 w (other) 1485 6312 w (objects) 1785 6312 w (for) 2177 6312 w (processors,) 2359 6312 w (thermal) 2961 6312 w (zones,) 3377 6312 w (mutexes,) 3739 6312 w (etc.) 4230 6312 w (Just) 4449 6312 w (keep) 4673 6312 w (in) 4949 6312 w (mind that they are not devices in ACPI.) 720 6432 w 10 /LucidaSans-Demi f (The interpreter) 720 6672 w 10 /LucidaSansUnicode00 f (The) 720 6828 w (AML) 947 6828 w (interpreter) 1201 6828 w (is) 1766 6828 w (a) 1892 6828 w (table) 1993 6828 w (driven) 2279 6828 w (implementation.) 2628 6828 w (It) 3500 6828 w (is) 3612 6828 w (a) 3738 6828 w (single) 3840 6828 w (thread) 4176 6828 w (that) 4537 6828 w (loops) 4775 6828 w (fetching) 720 6948 w (operation) 1155 6948 w (codes) 1659 6948 w (and) 1980 6948 w (executing) 2199 6948 w (operations) 2714 6948 w (according) 3269 6948 w (to) 3783 6948 w (them.) 3920 6948 w (Operation) 4238 6948 w (codes) 4758 6948 w (may) 720 7068 w (be) 962 7068 w (one) 1123 7068 w (or) 1344 7068 w (two) 1488 7068 w (bytes.) 1705 7068 w (Arguments) 2038 7068 w (depend) 2613 7068 w (not) 3018 7068 w (only) 3220 7068 w (on) 3466 7068 w (the) 3631 7068 w (operation) 3828 7068 w (code,) 4335 7068 w (but) 4640 7068 w (also) 4844 7068 w (on the state of the interpreter.) 720 7188 w cleartomark showpage saveobj restore %%EndPage: 7 7 %%Page: 8 8 /saveobj save def mark 8 pagesetup 10 /LucidaSansUnicode00 f (\255 8 \255) 2783 480 w (A) 970 840 w (table,) 1090 840 w 10 /LucidaTypewriter f (amlops) 1413 840 w 10 /LucidaSansUnicode00 f (,) 1845 840 w (provides) 1928 840 w (the) 2395 840 w (implementation) 2601 840 w (for) 3414 840 w (each) 3604 840 w (1-byte) 3879 840 w (operation) 4260 840 w (code.) 4777 840 w (Another) 720 960 w (table,) 1151 960 w 10 /LucidaTypewriter f (amlxops) 1466 960 w 10 /LucidaSansUnicode00 f (,) 1970 960 w (provides) 2045 960 w (the) 2504 960 w (implementation) 2702 960 w (for,) 3507 960 w (so) 3721 960 w (called,) 3876 960 w (extended) 4234 960 w (opera\255) 4731 960 w (tion codes. This is an excerpt:) 720 1080 w 9 /LucidaTypewriter f ([OpZero]) 920 1250 w ({opnum,) 1960 1250 w ("zero",) 3000 1250 w (Dry,) 4040 1250 w (""},) 4365 1250 w ([OpOne]) 920 1360 w ({opnum,) 1960 1360 w ("one",) 3000 1360 w (Dry,) 4040 1360 w (""},) 4365 1360 w ([OpAlias]) 920 1470 w ({opalias,) 1960 1470 w ("alias",) 3000 1470 w (0,) 4040 1470 w ("On"},) 4235 1470 w ([OpName]) 920 1580 w ({opname,) 1960 1580 w ("name",) 3000 1580 w (0,) 4040 1580 w ("nO"},) 4235 1580 w ([OpMid]) 920 1690 w ({opmid,) 1960 1690 w ("mid",) 3000 1690 w (0,) 4040 1690 w ("OIIt"},) 4235 1690 w 10 /LucidaSansUnicode00 f (Each entry is defined by this type:) 720 1906 w 9 /LucidaTypewriter f (typedef) 920 2076 w (void) 1440 2076 w (\(*Opx\)\(void\);) 1765 2076 w (/*) 920 2186 w (*) 985 2296 w (AML) 1115 2296 w (operation) 1375 2296 w (*/) 985 2406 w (struct) 920 2516 w (Op) 1375 2516 w ({) 920 2626 w (Opx) 1440 2736 w (x;) 1960 2736 w (/*) 3000 2736 w (implementation) 3195 2736 w (*/) 4170 2736 w (char*) 1440 2846 w (name;) 1960 2846 w (/*) 3000 2846 w (debug) 3195 2846 w (*/) 3585 2846 w (int) 1440 2956 w (flags;) 1960 2956 w (/*) 3000 2956 w (call) 3195 2956 w (on) 3520 2956 w (dry) 3715 2956 w (runs?) 3975 2956 w (Print) 4365 2956 w ({) 4755 2956 w (}) 4885 2956 w (on) 5015 2956 w (dumps?) 5210 2956 w (*/) 5665 2956 w (char*) 1440 3066 w (args;) 1960 3066 w (/*) 3000 3066 w (arguments,) 3195 3066 w (for) 3910 3066 w (AML) 4170 3066 w (parsing) 4430 3066 w (*/) 4950 3066 w (int) 1440 3176 w (ncalls;) 1960 3176 w (/*) 3000 3176 w (how) 3195 3176 w (many) 3455 3176 w (times) 3780 3176 w (used) 4170 3176 w (*/) 4495 3176 w (};) 920 3286 w 10 /LucidaSansUnicode00 f (The) 720 3502 w (implementation) 944 3502 w (assumes) 1750 3502 w (that) 2213 3502 w (each) 2448 3502 w (operation) 2716 3502 w (is) 3225 3502 w (responsible) 3349 3502 w (for) 3955 3502 w (building) 4138 3502 w (an) 4581 3502 w (object) 4742 3502 w (\(in) 720 3622 w (general\)) 902 3622 w (and) 1354 3622 w (may) 1592 3622 w (require) 1849 3622 w (arguments) 2254 3622 w (to) 2830 3622 w (perform) 2985 3622 w (its) 3434 3622 w (task.) 3608 3622 w (A) 3898 3622 w (string) 4024 3622 w (in) 4363 3622 w (each) 4511 3622 w (entry) 4792 3622 w (encodes) 720 3742 w (argument) 1159 3742 w (processing) 1666 3742 w (so) 2233 3742 w (that,) 2385 3742 w (after) 2648 3742 w (fetching) 2914 3742 w (an) 3350 3742 w (operation,) 3507 3742 w (the) 4044 3742 w 10 /LucidaTypewriter f (pargs) 4239 3742 w 10 /LucidaSansUnicode00 f (function) 4639 3742 w (may) 720 3862 w (parse) 962 3862 w (as) 1270 3862 w (much) 1418 3862 w (AML) 1728 3862 w (as) 1978 3862 w (needed) 2126 3862 w (to) 2524 3862 w (build) 2664 3862 w (argument) 2952 3862 w (objects) 3462 3862 w (as) 3853 3862 w (dictated) 4001 3862 w (by) 4434 3862 w (the) 4591 3862 w (argu\255) 4787 3862 w (ment) 720 3982 w (string.) 1029 3982 w (For) 1404 3982 w (example,) 1621 3982 w 10 /LucidaTypewriter f (opalias) 2127 3982 w 10 /LucidaSansUnicode00 f (requires) 2692 3982 w (an) 3152 3982 w (evaluated) 3330 3982 w (object) 3856 3982 w (and) 4215 3982 w (a) 4457 3982 w (name,) 4574 3982 w (as) 4934 3982 w (encoded by the string ) 720 4102 w 10 /LucidaSansUnicode20 f (\030\030) 1812 4102 w 10 /LucidaTypewriter f (On) 1876 4102 w 10 /LucidaSansUnicode20 f (\031\031) 2020 4102 w 10 /LucidaSansUnicode00 f (.) 2084 4102 w (In) 970 4258 w (the) 1098 4258 w (argument) 1290 4258 w (string,) 1795 4258 w (a) 2146 4258 w (lowercase) 2238 4258 w (refers) 2752 4258 w (to) 3071 4258 w (an) 3207 4258 w (object) 3362 4258 w (of) 3698 4258 w (a) 3834 4258 w (particular) 3927 4258 w (type) 4428 4258 w (with) 4674 4258 w (no) 4917 4258 w (evaluation) 720 4378 w (performed) 1252 4378 w (on) 1797 4378 w (it.) 1953 4378 w (An) 2084 4378 w (uppercase) 2248 4378 w (refers) 2779 4378 w (to) 3094 4378 w (an) 3225 4378 w (object) 3375 4378 w (that,) 3706 4378 w (after) 3962 4378 w (evaluation,) 4221 4378 w (has) 4784 4378 w (a) 4985 4378 w (particular) 720 4498 w (type.) 1229 4498 w (For) 1515 4498 w (example,) 1717 4498 w 10 /LucidaSansUnicode20 f (\030\030) 2208 4498 w 10 /LucidaTypewriter f (S) 2272 4498 w 10 /LucidaSansUnicode20 f (\031\031) 2344 4498 w 10 /LucidaSansUnicode00 f (means) 2454 4498 w (to) 2817 4498 w (parse) 2961 4498 w (an) 3273 4498 w (object,) 3436 4498 w (evaluate) 3812 4498 w (it,) 4260 4498 w (and) 4404 4498 w (obtain) 4631 4498 w (a) 4985 4498 w (string) 720 4618 w (object) 1039 4618 w (\(perhaps) 1374 4618 w (by) 1835 4618 w (doing) 1987 4618 w (a) 2301 4618 w (type) 2392 4618 w (cast) 2636 4618 w (to) 2866 4618 w (string\).) 3000 4618 w (On) 3383 4618 w (the) 3559 4618 w (other) 3750 4618 w (hand,) 4043 4618 w 10 /LucidaSansUnicode20 f (\030\030) 4353 4618 w 10 /LucidaTypewriter f (n) 4417 4618 w 10 /LucidaSansUnicode20 f (\031\031) 4489 4618 w 10 /LucidaSansUnicode00 f (means) 4589 4618 w (to) 4942 4618 w (parse an object that should be a name.) 720 4738 w (These characters may be used to specify arguments:) 970 4894 w 9 /LucidaTypewriter f (Obuf) 920 5064 w (=) 1440 5064 w ('B',) 1570 5064 w (/*) 2480 5064 w (buf) 2675 5064 w (*/) 2935 5064 w (Oevent) 920 5174 w (=) 1440 5174 w ('E',) 1570 5174 w (/*) 2480 5174 w (sleep/wakeup) 2675 5174 w (*/) 3520 5174 w (Ofield) 920 5284 w (=) 1440 5284 w ('F',,) 1570 5284 w (/*) 2480 5284 w (register) 2675 5284 w (field) 3260 5284 w (*/) 3650 5284 w (Oint) 920 5394 w (=) 1440 5394 w ('I',) 1570 5394 w (/*) 2480 5394 w (integer) 2675 5394 w (*/) 3195 5394 w (Oname) 920 5504 w (=) 1440 5504 w ('N',) 1570 5504 w (/*) 2480 5504 w (a) 2675 5504 w (name) 2805 5504 w (*/) 3130 5504 w (Omutex) 920 5614 w (=) 1440 5614 w ('M',) 1570 5614 w (/*) 2480 5614 w (mutex) 2675 5614 w (*/) 3065 5614 w (Opkg) 920 5724 w (=) 1440 5724 w ('P',) 1570 5724 w (/*) 2480 5724 w (package) 2675 5724 w (object) 3195 5724 w (\(array\)) 3650 5724 w (*/) 4170 5724 w (Oreg) 920 5834 w (=) 1440 5834 w ('R',) 1570 5834 w (/*) 2480 5834 w (data) 2675 5834 w (region) 3000 5834 w (*/) 3455 5834 w (Ostr) 920 5944 w (=) 1440 5944 w ('S',) 1570 5944 w (/*) 2480 5944 w (string) 2675 5944 w (*/) 3130 5944 w (Odecstr) 920 6054 w (=) 1440 6054 w ('D',) 1570 6054 w (/*) 2480 6054 w (like) 2675 6054 w (string,) 3000 6054 w (but) 3520 6054 w (bytes) 3780 6054 w (from) 4170 6054 w (buffer) 4495 6054 w (*/) 4950 6054 w (/*) 2480 6164 w (values) 2675 6164 w (are) 3130 6164 w (printed) 3390 6164 w (in) 3910 6164 w (decimal) 4105 6164 w (*/) 4625 6164 w (Oany) 920 6274 w (=) 1440 6274 w ('O',) 1570 6274 w (/*) 2480 6274 w (any) 2675 6274 w (object) 2935 6274 w (after) 3390 6274 w (resolving) 3780 6274 w (names) 4430 6274 w (*/) 4820 6274 w 10 /LucidaSansUnicode00 f (These) 720 6490 w (other) 1041 6490 w (characters,) 1331 6490 w (quoted) 1897 6490 w (from) 2273 6490 w (the) 2539 6490 w (implementation,) 2728 6490 w (may) 3556 6490 w (be) 3790 6490 w (used) 3943 6490 w (but) 4209 6490 w (do) 4405 6490 w (not) 4563 6490 w (corre\255) 4757 6490 w (spond) 720 6610 w (to) 1053 6610 w (any) 1184 6610 w (object) 1386 6610 w (type) 1717 6610 w (as) 1958 6610 w (known) 2097 6610 w (by) 2450 6610 w (the) 2598 6610 w (interpreter) 2786 6610 w (\(they) 3338 6610 w (build) 3611 6610 w (objects,) 3890 6610 w (but) 4304 6610 w (do not rep\255) 4499 6610 w (resent different object types\):) 720 6730 w cleartomark showpage saveobj restore %%EndPage: 8 8 %%Page: 9 9 /saveobj save def mark 9 pagesetup 10 /LucidaSansUnicode00 f (\255 9 \255) 2783 480 w 9 /LucidaTypewriter f (/*) 920 830 w (*) 985 940 w (-) 1440 940 w ('l') 1570 940 w (means) 1830 940 w (packagelen) 2220 940 w (*) 985 1050 w (-) 1440 1050 w ('b') 1570 1050 w (means) 1830 1050 w (byte,) 2220 1050 w ('w') 2610 1050 w (word,) 2870 1050 w ('d') 3260 1050 w (d-word,) 3520 1050 w (*) 985 1160 w (-) 1440 1160 w ('f') 1570 1160 w (fieldlist) 1830 1160 w (*) 985 1270 w (-) 1440 1270 w ('s') 1570 1270 w (string) 1830 1270 w (*) 985 1380 w (-) 1440 1380 w ('n') 1570 1380 w (name) 1830 1380 w (or) 2155 1380 w (arg) 2350 1380 w (or) 2610 1380 w (local) 2805 1380 w (*) 985 1490 w (-) 1440 1490 w ('t') 1570 1490 w (target) 1830 1490 w (\(name,) 2285 1490 w (perhaps) 2740 1490 w (created,) 3260 1490 w (dst) 3845 1490 w (object,) 4105 1490 w (nil\)) 4625 1490 w (*) 985 1600 w (-) 1440 1600 w ('o') 1570 1600 w (any) 1830 1600 w (object,) 2090 1600 w (maybe) 2610 1600 w (a) 3000 1600 w (name.) 3130 1600 w (*) 985 1710 w (Upper) 1115 1710 w (case) 1505 1710 w (args) 1830 1710 w (are) 2155 1710 w (evaluated) 2415 1710 w (by) 3065 1710 w (pargs) 3260 1710 w (to) 3650 1710 w (obtain) 3845 1710 w (the) 4300 1710 w (typed) 4560 1710 w (arg.) 4950 1710 w (*) 985 1820 w (Other) 1115 1820 w (args) 1505 1820 w (are) 1830 1820 w (not) 2090 1820 w (evaluated) 2350 1820 w (before) 3000 1820 w (calling) 3455 1820 w (the) 3975 1820 w (operation.) 4235 1820 w (*/) 985 1930 w 10 /LucidaSansUnicode00 f (But) 720 2146 w (for) 920 2146 w (the) 1102 2146 w (pointer) 1300 2146 w (to) 1692 2146 w (the) 1833 2146 w (implementation) 2031 2146 w (for) 2836 2146 w (each) 3018 2146 w (operation) 3285 2146 w (and) 3793 2146 w (the) 4016 2146 w (argument) 4214 2146 w (string,) 4726 2146 w (the) 720 2266 w (only) 919 2266 w (interesting) 1167 2266 w (information) 1733 2266 w (in) 2344 2266 w (the) 2479 2266 w (operation) 2678 2266 w (table) 3187 2266 w (is) 3471 2266 w (a) 3595 2266 w (flag) 3694 2266 w (to) 3921 2266 w (see) 4062 2266 w (if) 4268 2266 w (the) 4377 2266 w (operation) 4575 2266 w (must) 720 2386 w (be) 1006 2386 w (called) 1168 2386 w (on) 1494 2386 w (dry) 1660 2386 w (runs) 1859 2386 w (or) 2118 2386 w (not) 2263 2386 w (and) 2466 2386 w (a) 2689 2386 w (flag) 2787 2386 w (to) 3013 2386 w (help) 3154 2386 w (pretty-print) 3407 2386 w (\(sic\)) 4026 2386 w (AML) 4266 2386 w (for) 4518 2386 w (debug\255) 4701 2386 w (ging.) 720 2506 w (The main entry point for the interpreter is) 970 2662 w 9 /LucidaTypewriter f (char*) 920 2832 w (amleval\(uchar) 920 2942 w (*ps,) 1830 2942 w (uchar) 2155 2942 w (*pe,) 2545 2942 w (Aobj) 2870 2942 w (**po,) 3195 2942 w (Name) 3585 2942 w (*n,) 3910 2942 w (Atable*) 4170 2942 w (table,) 4690 2942 w (int) 5145 2942 w (flags\)) 5405 2942 w 10 /LucidaSansUnicode00 f (which) 720 3158 w (evaluates) 1041 3158 w (AML) 1534 3158 w (code) 1782 3158 w (between) 2053 3158 w (pointers) 2501 3158 w 10 /LucidaTypewriter f (ps) 2942 3158 w 10 /LucidaSansUnicode00 f (and) 3127 3158 w 10 /LucidaTypewriter f (ps) 3348 3158 w 10 /LucidaSansUnicode00 f (using) 3533 3158 w 10 /LucidaTypewriter f (n) 3840 3158 w 10 /LucidaSansUnicode00 f (as) 3953 3158 w (the) 4100 3158 w (namespace) 4296 3158 w (dot) 4879 3158 w (for the evaluation. It returns an error string and the resulting object in) 720 3278 w 10 /LucidaTypewriter f (*po) 4168 3278 w 10 /LucidaSansUnicode00 f (.) 4384 3278 w (Another function evaluates a single call to a method object \(just a subroutine\):) 970 3434 w 9 /LucidaTypewriter f (Aobj*) 920 3604 w (amlcall\(Aobj) 920 3714 w (*mo,) 1765 3714 w (Aobj) 2090 3714 w (**args,) 2415 3714 w (Atable) 2935 3714 w (*table,) 3390 3714 w (int) 3910 3714 w (flags\)) 4170 3714 w 10 /LucidaSansUnicode00 f (It accepts arguments for the method call and returns a result object.) 720 3930 w (Both) 970 4086 w (functions) 1228 4086 w (are) 1720 4086 w (implemented) 1912 4086 w (by) 2589 4086 w (calling) 2744 4086 w 10 /LucidaTypewriter f (pamlblock) 3101 4086 w 10 /LucidaSansUnicode00 f (,) 3749 4086 w (which) 3821 4086 w (parses) 4142 4086 w (\(and) 4499 4086 w (evalu\255) 4753 4086 w (ates\)) 720 4206 w (an) 997 4206 w (AML) 1159 4206 w (block.) 1412 4206 w 10 /LucidaTypewriter f (Pamlblock) 1783 4206 w 10 /LucidaSansUnicode00 f (is) 2476 4206 w (mostly) 2600 4206 w (a) 2967 4206 w (loop) 3066 4206 w (that) 3324 4206 w (calls) 3559 4206 w 10 /LucidaTypewriter f (pamlobj) 3818 4206 w 10 /LucidaSansUnicode00 f (,) 4322 4206 w (which) 4398 4206 w (parses) 4723 4206 w (\(and evaluates\) as much AML as needed to build a single object.) 720 4326 w (Depending) 970 4482 w (on) 1540 4482 w (the) 1705 4482 w (flags) 1902 4482 w (given) 2178 4482 w (to) 2481 4482 w (the) 2621 4482 w (evaluator,) 2818 4482 w (it) 3340 4482 w (may) 3448 4482 w (just) 3690 4482 w (dissasemble) 3912 4482 w (AML,) 4551 4482 w (exe\255) 4834 4482 w (cute it, print actions as they are performed, and perform various debug checks:) 720 4602 w 10 /LucidaTypewriter f (Edryrun) 720 4758 w 10 /LucidaSansUnicode00 f (asks for a dry run. Usually combined with other flags that dump information.) 970 4878 w 10 /LucidaTypewriter f (Eidump) 720 5034 w 10 /LucidaSansUnicode00 f (asks for instruction dumps. This is close to a disassembler.) 970 5154 w 10 /LucidaTypewriter f (Exdump) 720 5310 w 10 /LucidaSansUnicode00 f (asks for messages about actions performed.) 970 5430 w 10 /LucidaTypewriter f (Escheck) 720 5586 w 10 /LucidaSansUnicode00 f (asks) 970 5706 w (for) 1221 5706 w (full) 1397 5706 w (stack) 1591 5706 w (checks) 1880 5706 w (to) 2246 5706 w (ensure) 2381 5706 w (that) 2746 5706 w (reference) 2974 5706 w (counting) 3467 5706 w (is) 3930 5706 w (correct) 4047 5706 w (and) 4422 5706 w (to) 4639 5706 w (see) 4774 5706 w (if) 4974 5706 w (objects) 970 5826 w (seem) 1369 5826 w (to) 1674 5826 w (be) 1821 5826 w (correct.) 1989 5826 w (This) 2408 5826 w (slows) 2662 5826 w (down) 2980 5826 w (execution) 3292 5826 w (a) 3816 5826 w (lot,) 3920 5826 w (but) 4128 5826 w (is) 4339 5826 w (useful) 4468 5826 w (after) 4814 5826 w (making) 970 5946 w (changes) 1370 5946 w (to) 1810 5946 w (the) 1949 5946 w (interpreter,) 2145 5946 w (because) 2737 5946 w (it) 3172 5946 w (is) 3279 5946 w (easy) 3400 5946 w (to) 3655 5946 w (make) 3794 5946 w (mistakes) 4097 5946 w (regarding) 4569 5946 w (object references.) 970 6066 w (When) 720 6222 w (the) 1040 6222 w (interpreter) 1249 6222 w (finds) 1822 6222 w (a) 2118 6222 w (method) 2227 6222 w (definition) 2653 6222 w (is) 3172 6222 w (skips) 3306 6222 w (its) 3612 6222 w (implementation) 3784 6222 w (and) 4601 6222 w (only) 4836 6222 w (records) 720 6342 w (where) 1126 6342 w (the) 1459 6342 w (code) 1655 6342 w (starts) 1927 6342 w (and) 2240 6342 w (its) 2461 6342 w (length.) 2619 6342 w (Method) 3000 6342 w (evaluation) 3406 6342 w (is) 3945 6342 w (performed) 4066 6342 w (by) 4618 6342 w (using) 4774 6342 w 10 /LucidaTypewriter f (pamlblock) 720 6462 w 10 /LucidaSansUnicode00 f (on that part of the program.) 1400 6462 w 10 /LucidaSans-Demi f (Environments) 720 6702 w 10 /LucidaTypewriter f (Env) 720 6894 w 10 /LucidaSansUnicode00 f (represents) 993 6894 w (an) 1564 6894 w (evaluation) 1738 6894 w (environment) 2293 6894 w (at) 2961 6894 w (a) 3110 6894 w (particular) 3222 6894 w (scope.) 3742 6894 w (Multiple) 4114 6894 w 10 /LucidaTypewriter f (Env) 4563 6894 w 10 /LucidaSansUnicode00 f (s) 4779 6894 w (are) 4888 6894 w (nested) 720 7014 w (as) 1092 7014 w (a) 1245 7014 w (result) 1346 7014 w (of) 1668 7014 w (entering) 1812 7014 w (and) 2263 7014 w (leaving) 2489 7014 w (scopes) 2880 7014 w (in) 3259 7014 w (the) 3396 7014 w (AML) 3597 7014 w (program.) 3851 7014 w (This) 4377 7014 w (happens) 4628 7014 w (both) 720 7134 w (to) 988 7134 w (implement) 1131 7134 w (actual) 1694 7134 w (AML) 2028 7134 w (scopes) 2281 7134 w (and) 2659 7134 w (also) 2884 7134 w (to) 3125 7134 w (open) 3269 7134 w (temporary) 3557 7134 w (scopes) 4102 7134 w (to) 4481 7134 w (save) 4625 7134 w (the) 4885 7134 w (current) 720 7254 w (environment) 1108 7254 w (\(e.g.,) 1757 7254 w (during) 2042 7254 w (argument) 2399 7254 w (evaluation\).) 2905 7254 w (All) 3505 7254 w 10 /LucidaTypewriter f (Env) 3669 7254 w 10 /LucidaSansUnicode00 f (structures) 3922 7254 w (live) 4448 7254 w (in) 4651 7254 w (the) 4779 7254 w (C) 4971 7254 w cleartomark showpage saveobj restore %%EndPage: 9 9 %%Page: 10 10 /saveobj save def mark 10 pagesetup 10 /LucidaSansUnicode00 f (\255 10 \255) 2752 480 w (stack.) 720 840 w (A) 970 996 w (global) 1074 996 w (variable,) 1408 996 w 10 /LucidaTypewriter f (amlenv) 1855 996 w 10 /LucidaSansUnicode00 f (,) 2287 996 w (points) 2354 996 w (to) 2692 996 w (the) 2825 996 w (current) 3015 996 w (\(i.e.,) 3400 996 w (last,) 3649 996 w (or) 3888 996 w (inner-most\)) 4025 996 w (environ\255) 4644 996 w (ment.) 720 1116 w (This) 1033 1116 w 10 /LucidaTypewriter f (Env) 1271 1116 w 10 /LucidaSansUnicode00 f (can) 1520 1116 w (be) 1721 1116 w (considered) 1873 1116 w (the) 2439 1116 w (state) 2627 1116 w (of) 2896 1116 w (the) 3027 1116 w (abstract) 3215 1116 w (machine) 3638 1116 w (and) 4079 1116 w (is) 4292 1116 w (implicit to all) 4405 1116 w (AML operations.) 720 1236 w 9 /LucidaTypewriter f (struct) 920 1406 w (Env) 1375 1406 w ({) 920 1516 w (char*) 1440 1626 w (name;) 1960 1626 w (/*) 3000 1626 w (debug) 3195 1626 w (*/) 3585 1626 w (Env*) 1440 1736 w (prev;) 1960 1736 w (/*) 3000 1736 w (in) 3195 1736 w (env) 3390 1736 w (stack) 3650 1736 w (*/) 4040 1736 w (int) 1440 1846 w (ic;) 1960 1846 w (/*) 3000 1846 w (instruction) 3195 1846 w (code) 3975 1846 w (*/) 4300 1846 w (uchar*) 1440 1956 w (ps;) 1960 1956 w (/*) 3000 1956 w (start) 3195 1956 w (of) 3585 1956 w (what's) 3780 1956 w (left) 4235 1956 w (of) 4560 1956 w (program) 4755 1956 w (\(PC\)) 5275 1956 w (*/) 5600 1956 w (uchar*) 1440 2066 w (pe;) 1960 2066 w (/*) 3000 2066 w (end) 3195 2066 w (of) 3455 2066 w (program) 3650 2066 w (*/) 4170 2066 w (int) 1440 2176 w (lvl;) 1960 2176 w (/*) 3000 2176 w (env) 3195 2176 w (nesting) 3455 2176 w (level) 3975 2176 w (\(debug\)) 4365 2176 w (*/) 4885 2176 w (Op*) 1440 2396 w (op;) 1960 2396 w (/*) 3000 2396 w (current) 3195 2396 w (op) 3715 2396 w (*/) 3910 2396 w (uchar*) 1440 2506 w (opp0;) 1960 2506 w (/*) 3000 2506 w (operation) 3195 2506 w (address) 3845 2506 w (0) 4365 2506 w (*/) 4495 2506 w (Nlist) 1440 2616 w (olist;) 1960 2616 w (/*) 3000 2616 w (list) 3195 2616 w (of) 3520 2616 w (obj) 3715 2616 w (in) 3975 2616 w (env) 4170 2616 w (*/) 4430 2616 w (Aobj*) 1440 2726 w (args[Nargs];) 1960 2726 w (/*) 3000 2726 w (arguments) 3195 2726 w (for) 3845 2726 w (op) 4105 2726 w (*/) 4300 2726 w (int) 1440 2836 w (nargs;) 1960 2836 w (/*) 3000 2836 w (max) 3195 2836 w (number) 3455 2836 w (of) 3910 2836 w (args) 4105 2836 w (used) 4430 2836 w (*/) 4755 2836 w (uchar*) 1440 2946 w (argpe;) 1960 2946 w (/*) 3000 2946 w (pe) 3195 2946 w (resulting) 3390 2946 w (from) 4040 2946 w (arg) 4365 2946 w ('l') 4625 2946 w (*/) 4885 2946 w (Aobj*) 1440 3056 w (res;) 1960 3056 w (/*) 3000 3056 w (result) 3195 3056 w (from) 3650 3056 w (op) 3975 3056 w (*/) 4170 3056 w (Mframe*) 1440 3276 w (mp;) 1960 3276 w (/*) 3000 3276 w (Method) 3195 3276 w (frame) 3650 3276 w (pointer) 4040 3276 w (*/) 4560 3276 w (Aname*) 1440 3386 w (dot;) 1960 3386 w (/*) 3000 3386 w (current) 3195 3386 w (acpi) 3715 3386 w (name) 4040 3386 w (*/) 4365 3386 w (Atable*) 1440 3496 w (table;) 1960 3496 w (/*) 3000 3496 w ([ds]sdt) 3195 3496 w (table) 3715 3496 w (id) 4105 3496 w (*/) 4300 3496 w (int) 1440 3606 w (flags;) 1960 3606 w (};) 920 3716 w 10 /LucidaSansUnicode00 f (As) 720 3932 w (said,) 891 3932 w (the) 1172 3932 w (implementation) 1378 3932 w (looks) 2191 3932 w (up) 2502 3932 w (an) 2678 3932 w (operation) 2846 3932 w (code) 3362 3932 w (at) 3644 3932 w (a) 3787 3932 w (time) 3893 3932 w (and) 4159 3932 w (processes) 4390 3932 w (its) 4923 3932 w (arguments) 720 4052 w (as) 1293 4052 w (indicated) 1453 4052 w (by) 1952 4052 w (an) 2121 4052 w (argument) 2292 4052 w (string.) 2814 4052 w (Some) 3182 4052 w (arguments) 3500 4052 w (are) 4073 4052 w (simple) 4279 4052 w (and) 4654 4052 w (are) 4888 4052 w (parsed) 720 4172 w (directly.) 1092 4172 w (Others) 1526 4172 w (are) 1895 4172 w (not) 2091 4172 w (and) 2295 4172 w (may) 2519 4172 w (change) 2763 4172 w (the) 3155 4172 w (environment) 3354 4172 w (while) 4009 4172 w (they) 4306 4172 w (are) 4557 4172 w (evalu\255) 4753 4172 w (ated.) 720 4292 w (In) 1008 4292 w (this) 1144 4292 w (case,) 1368 4292 w (a) 1658 4292 w (nested) 1758 4292 w (environment) 2128 4292 w (is) 2784 4292 w (pushed) 2909 4292 w (to) 3311 4292 w (protect) 3454 4292 w (the) 3845 4292 w (environment) 4045 4292 w (for) 4701 4292 w (the) 4885 4292 w (operation) 720 4412 w (\(so) 1222 4412 w (that) 1404 4412 w (its) 1633 4412 w (implementation) 1788 4412 w (may) 2588 4412 w (assume) 2826 4412 w (that) 3232 4412 w (the) 3461 4412 w (environment) 3654 4412 w (corresponds) 4303 4412 w (to) 4942 4412 w (the operation and has not been altered much during argument processing\).) 720 4532 w (When an operation is called:) 970 4688 w 10 /LucidaTypewriter f (amlenv->op) 720 4844 w 10 /LucidaSansUnicode00 f (points to the operation being executed \(its table entry\).) 970 4964 w 10 /LucidaTypewriter f (amlenv->ic) 720 5120 w 10 /LucidaSansUnicode00 f (is the instruction code for the operation \(the second byte for two-byte operations\).) 970 5240 w 10 /LucidaTypewriter f (amlenv->ps) 720 5396 w 10 /LucidaSansUnicode00 f (is the program counter \(the start of what) 970 5516 w 10 /LucidaSansUnicode20 f (\031) 2953 5516 w 10 /LucidaSansUnicode00 f (s left of the program\). When the) 2985 5516 w (operation) 4575 5516 w (includes) 970 5636 w (an) 1412 5636 w (argument) 1568 5636 w (string,) 2075 5636 w (it) 2428 5636 w (points) 2533 5636 w (past) 2874 5636 w (the) 3118 5636 w (AML) 3311 5636 w (code) 3557 5636 w (for) 3826 5636 w (the) 4003 5636 w (arguments.) 4196 5636 w (Upon) 4785 5636 w (return,) 970 5756 w (it) 1348 5756 w (must) 1461 5756 w (point) 1751 5756 w (past) 2050 5756 w (the) 2303 5756 w (last) 2505 5756 w (byte) 2724 5756 w (of) 2979 5756 w (AML) 3124 5756 w (code) 3379 5756 w (for) 3657 5756 w (the) 3843 5756 w (operation) 4045 5756 w (\(including) 4558 5756 w (arguments) 970 5876 w (and) 1529 5876 w (body,) 1749 5876 w (if) 2059 5876 w (any\).) 2164 5876 w (N.B.:) 2469 5876 w (the) 2736 5876 w (implementation) 2930 5876 w (for) 3731 5876 w 10 /LucidaTypewriter f (opwhile) 3909 5876 w 10 /LucidaSansUnicode00 f (violates) 4452 5876 w (this) 4861 5876 w (assumption for a good reason.) 970 5996 w (Operations) 970 6152 w (with) 1556 6152 w (implementations) 1814 6152 w (that) 2680 6152 w (are) 2925 6152 w (also) 3131 6152 w (used) 3381 6152 w (to) 3667 6152 w (parse) 3819 6152 w (arguments) 4139 6152 w (should) 4712 6152 w (look) 970 6272 w (at) 1240 6272 w 10 /LucidaTypewriter f (amlenv->ps[0]) 1393 6272 w 10 /LucidaSansUnicode00 f (and) 2390 6272 w (not) 2631 6272 w (to) 2852 6272 w 10 /LucidaTypewriter f (amlenv->ic) 3011 6272 w 10 /LucidaSansUnicode00 f (to) 3792 6272 w (see) 3951 6272 w (their) 4175 6272 w (IC.) 4461 6272 w (That) 4683 6272 w (is) 4960 6272 w (because) 970 6392 w (the) 1397 6392 w (IC) 1585 6392 w (is) 1716 6392 w (assumed) 1829 6392 w (to) 2293 6392 w (correspond) 2424 6392 w (to) 3007 6392 w (the) 3138 6392 w (operation) 3326 6392 w (itself) 3824 6392 w (and) 4096 6392 w (not) 4310 6392 w (to) 4504 6392 w (its) 4636 6392 w (argu\255) 4787 6392 w (ments.) 970 6512 w 10 /LucidaTypewriter f (amlenv->pe) 720 6668 w 10 /LucidaSansUnicode00 f (is) 970 6788 w (the) 1099 6788 w (end) 1303 6788 w (of) 1533 6788 w (the) 1680 6788 w (program.) 1884 6788 w (Operations) 2381 6788 w (may) 2963 6788 w (include) 3212 6788 w (a) 3613 6788 w (length) 3717 6788 w (argument) 4074 6788 w (that) 4592 6788 w (con\255) 4833 6788 w (straints) 970 6908 w (the) 1368 6908 w (program) 1558 6908 w (size) 2009 6908 w (\(e.g.,) 2237 6908 w (to) 2519 6908 w (define) 2652 6908 w (a) 2990 6908 w (portion) 3080 6908 w (of) 3468 6908 w (code) 3600 6908 w (as) 3865 6908 w (the) 4005 6908 w 10 /LucidaSans-Italic f (then) 4194 6908 w 10 /LucidaSansUnicode00 f (-arm) 4409 6908 w (of) 4690 6908 w (an) 4822 6908 w 10 /LucidaSans-Italic f (if) 4973 6908 w 10 /LucidaSansUnicode00 f (instruction\).) 970 7028 w 10 /LucidaTypewriter f (amlenv->opp0) 720 7184 w 10 /LucidaSansUnicode00 f (is) 970 7304 w (the) 1103 7304 w (program) 1311 7304 w (counter) 1780 7304 w (for) 2203 7304 w (the) 2395 7304 w (current) 2603 7304 w (operation) 3006 7304 w (\(the) 3524 7304 w (zero) 3765 7304 w (address) 4034 7304 w (for) 4468 7304 w (its) 4661 7304 w (AML) 4832 7304 w cleartomark showpage saveobj restore %%EndPage: 10 10 %%Page: 11 11 /saveobj save def mark 11 pagesetup 10 /LucidaSansUnicode00 f (\255 11 \255) 2752 480 w (code\). This is important to) 970 840 w 10 /LucidaTypewriter f (opwhile) 2287 840 w 10 /LucidaSansUnicode00 f (.) 2791 840 w (When operations with argument strings are called:) 720 996 w 10 /LucidaTypewriter f (amlenv->ps) 720 1152 w 10 /LucidaSansUnicode00 f (is past the arguments.) 970 1272 w 10 /LucidaTypewriter f (amlenv->args) 720 1428 w 10 /LucidaSansUnicode00 f (includes pointers to argument objects for the operation.) 970 1548 w 10 /LucidaTypewriter f (amlenv->argpe) 720 1704 w 10 /LucidaSansUnicode00 f (is) 970 1824 w (the) 1086 1824 w (end) 1277 1824 w (of) 1494 1824 w (the) 1628 1824 w (program) 1819 1824 w (according) 2271 1824 w (to) 2783 1824 w (the) 2918 1824 w (length) 3110 1824 w (argument.) 3455 1824 w (The) 3992 1824 w (operation) 4210 1824 w (should) 4712 1824 w (not touch AML code past it.) 970 1944 w (Upon return from an operation:) 720 2100 w 10 /LucidaTypewriter f (amlenv->res) 720 2256 w 10 /LucidaSansUnicode00 f (points to the object returned from the operation \(perhaps) 970 2376 w 10 /LucidaTypewriter f (nil) 3806 2376 w 10 /LucidaSansUnicode00 f (\).) 4022 2376 w (Arguments) 720 2532 w (and) 1294 2532 w (locals) 1515 2532 w (are) 1832 2532 w (closed) 2025 2532 w (when) 2377 2532 w (the) 2675 2532 w (operation) 2871 2532 w (returns.) 3378 2532 w (When) 3802 2532 w (the) 4110 2532 w (environment) 4307 2532 w (is) 4960 2532 w (terminated) 720 2652 w (\(pop\)) 1286 2652 w (or) 1576 2652 w (the) 1715 2652 w (next) 1906 2652 w (operation) 2158 2652 w (executes,) 2659 2652 w 10 /LucidaTypewriter f (amlenv->res) 3157 2652 w 10 /LucidaSansUnicode00 f (is) 3985 2652 w (closed.) 4101 2652 w (Thus,) 4480 2652 w (oper\255) 4786 2652 w (ations) 720 2772 w (that) 1060 2772 w (don) 1296 2772 w 10 /LucidaSansUnicode20 f (\031) 1482 2772 w 10 /LucidaSansUnicode00 f (t) 1514 2772 w (have) 1596 2772 w (to) 1866 2772 w (return) 2009 2772 w (a) 2353 2772 w (value) 2453 2772 w (may) 2752 2772 w (leave) 2997 2772 w (an) 3290 2772 w (object) 3453 2772 w (linked) 3797 2772 w (there,) 4140 2772 w (so) 4470 2772 w (it) 4628 2772 w (is) 4740 2772 w (col\255) 4866 2772 w (lected upon errors.) 720 2892 w (Environments used to evaluate methods include a pointer to an) 970 3048 w 10 /LucidaTypewriter f (Mframe) 4076 3048 w 10 /LucidaSansUnicode00 f (.) 4508 3048 w 9 /LucidaTypewriter f (/*) 920 3218 w (*) 985 3328 w (Method) 1115 3328 w (activation) 1570 3328 w (frame.) 2285 3328 w (*/) 985 3438 w (struct) 920 3548 w (Mframe) 1375 3548 w ({) 920 3658 w (Aobj*) 1440 3768 w (args[Nmargs];) 1960 3768 w (/*) 3000 3768 w (method) 3195 3768 w (arguments) 3650 3768 w (*/) 4300 3768 w (int) 1440 3878 w (nargs;) 1960 3878 w (Aobj*) 1440 3988 w (lcls[Nmlcls];) 1960 3988 w (/*) 3000 3988 w (method) 3195 3988 w (locals) 3650 3988 w (*/) 4105 3988 w (int) 1440 4098 w (nlcls;) 1960 4098 w (Aobj**) 1440 4208 w (targs;) 1960 4208 w (/*) 3000 4208 w (tmp.) 3195 4208 w (method) 3520 4208 w (args) 3975 4208 w (while) 4300 4208 w (parsing) 4690 4208 w (them) 5210 4208 w (*/) 5535 4208 w (Aname*) 1440 4318 w (binds;) 1960 4318 w (/*) 3000 4318 w (done) 3195 4318 w (by) 3520 4318 w (this) 3715 4318 w (method) 4040 4318 w (call) 4495 4318 w (*/) 4820 4318 w (};) 920 4428 w 10 /LucidaSansUnicode00 f (This) 720 4608 w (is) 959 4608 w (just) 1073 4608 w (a) 1287 4608 w (place) 1376 4608 w (to) 1664 4608 w (store) 1797 4608 w (method) 2078 4608 w (arguments) 2485 4608 w (and) 3039 4608 w (locals,) 3254 4608 w (as) 3597 4608 w (well) 3738 4608 w (as) 3964 4608 w (a) 4105 4608 w (place) 4195 4608 w (to) 4484 4608 w (keep) 4617 4608 w (the) 4885 4608 w (list) 720 4728 w (of) 902 4728 w (names) 1036 4728 w (bound) 1388 4728 w (by) 1734 4728 w (a) 1884 4728 w (method) 1974 4728 w (call) 2381 4728 w (\(which) 2580 4728 w (must) 2929 4728 w (be) 3207 4728 w (unbound) 3361 4728 w (upon) 3831 4728 w (returns\).) 4114 4728 w (A) 4564 4728 w (method) 4668 4728 w (call) 720 4848 w (pushes) 919 4848 w (a) 1299 4848 w (new) 1389 4848 w 10 /LucidaTypewriter f (Env) 1619 4848 w 10 /LucidaSansUnicode00 f (\(in) 1870 4848 w (the) 2029 4848 w (C) 2219 4848 w (stack\)) 2323 4848 w (that) 2643 4848 w (points) 2869 4848 w (to) 3207 4848 w (a) 3340 4848 w (new) 3430 4848 w 10 /LucidaTypewriter f (Mframe) 3660 4848 w 10 /LucidaSansUnicode00 f (\(in) 4127 4848 w (the) 4287 4848 w (C) 4478 4848 w (stack\)) 4583 4848 w (via) 4904 4848 w 10 /LucidaTypewriter f (mp) 720 4968 w 10 /LucidaSansUnicode00 f (.) 864 4968 w (Further scopes entered inherit the) 960 4968 w 10 /LucidaTypewriter f (mp) 2648 4968 w 10 /LucidaSansUnicode00 f (pointer from the parent) 2824 4968 w 10 /LucidaTypewriter f (Env) 4002 4968 w 10 /LucidaSansUnicode00 f (.) 4218 4968 w 10 /LucidaSans-Demi f (Error handling) 720 5208 w 10 /LucidaSansUnicode00 f (Remember) 720 5364 w (that) 1296 5364 w (there) 1542 5364 w (are) 1849 5364 w (no) 2056 5364 w (processes) 2234 5364 w (by) 2770 5364 w (the) 2940 5364 w (time) 3150 5364 w (the) 3421 5364 w (interpreter) 3632 5364 w (runs.) 4207 5364 w (Errors) 4543 5364 w (are) 4888 5364 w (reported using an error stack, similar to that used by processes.) 720 5484 w (This is an example:) 3905 5484 w 9 /LucidaTypewriter f (static) 920 5654 w (void) 1375 5654 w (oppackage\(void\)) 920 5764 w ({) 920 5874 w (Env) 1440 5984 w (env;) 1700 5984 w (...) 1440 6094 w (amlpush\(&env,) 1440 6204 w (amlenv->op->name,) 2350 6204 w (nil,) 3520 6204 w (amlenv->argpe\);) 3845 6204 w (if\(wasamlerror\(\)\){) 1440 6314 w (amlpop\(\);) 1960 6424 w (amlerror\(nil\);) 1960 6534 w (/*) 3000 6534 w (pass) 3195 6534 w (a) 3520 6534 w (string) 3650 6534 w (to) 4105 6534 w (raise) 4300 6534 w (it) 4690 6534 w (*/) 4885 6534 w (}) 1440 6644 w (pamlblock\(Keep\);) 1440 6754 w (...) 1440 6864 w (popamlerror\(\);) 1440 6974 w (amlpop\(\);) 1440 7084 w (}) 920 7194 w cleartomark showpage saveobj restore %%EndPage: 11 11 %%Page: 12 12 /saveobj save def mark 12 pagesetup 10 /LucidaSansUnicode00 f (\255 12 \255) 2752 480 w (Operations) 720 840 w 10 /LucidaTypewriter f (break) 1288 840 w 10 /LucidaSansUnicode00 f (,) 1648 840 w 10 /LucidaTypewriter f (continue) 1715 840 w 10 /LucidaSansUnicode00 f (,) 2291 840 w (and) 2358 840 w 10 /LucidaTypewriter f (return) 2573 840 w 10 /LucidaSansUnicode00 f (raise) 3040 840 w (their) 3307 840 w (names) 3567 840 w (and) 3919 840 w (rely) 4134 840 w (on) 4347 840 w (this) 4505 840 w (excep\255) 4720 840 w (tion mechanism for their implementation.) 720 960 w cleartomark showpage saveobj restore %%EndPage: 12 12 %%Trailer done %%DocumentFonts: LucidaSansUnicode20 LucidaSansUnicode22 LucidaSansUnicode00 LucidaSans-Demi LucidaSans-Italic LucidaTypewriter %%Pages: 12 ry\).) 970 4964 w 10 /LucidaTypewriter f (amlenv->ic) 720 5120 w 10 9am/amlconv.c 664 0 0 32534 11323204504 11176ustar00nemosys/* * AML conversion, copy, and I/O routines. * ACPI has all kinds of requirements on casts and * assignments. It is easy to make mistakes there and * this file keeps that code apart. * * Regarding I/O, we must support different regions with * different alignment constraints and preserving rules. * Only some of the required regions are here. Others are missing. */ #include "kernel.h" #include "acpi.h" #include "aml.h" static void fieldwr(Aobj*,Aobj*); static void fieldio(Aobj *o, void *a, int iswr); static Aobj*tcast(Aobj*, int); static Aobj*tobuf(Aobj*); static Aobj*dupobj(Aobj*, Aobj*); /* * bit numbers: [7-0] [15-8] ... */ static void bitset(void *d, int dbit, int val) { char *dp; dp = d; dp += dbit >> 3; dbit &= 7; if(val != 0) *dp |= 1<> 3; dbit &= 7; return (*dp & 1< 7){ n = len >> 3; memmove(dp + (dbit>>3), sp + (sbit>>3), n); dbit += n<<3; sbit += n<<3; len -= n<<3; } /* copy remaining bits */ for(i = 0; i < len; i++) bitset(dp, dbit++, bitget(sp, sbit++)); } static void idxfieldio(Aobj *o, void *a, int iswr) { Aobj *data; uchar *p; int nbytes; amltcheck(o->field.idx, Ofield, 0); data = o->field.data; amltcheck(data, Ofield, 0); fieldwr(o->field.idx, o->field.idxval); nbytes = (data->field.len+7)/8; p = amlmalloc(nbytes); if(wasamlerror()){ free(p); amlerror(nil); } if(iswr && (o->field.off != 0 || o->field.len != nbytes*8)) switch(o->field.update){ case AFwrones: memset(p, ~0, nbytes); break; case AFpreserve: fieldio(data, p, 0); break; } if(iswr){ bitcpy(p, o->field.off, a, 0, o->field.len); fieldio(data, p, 1); }else{ fieldio(data, p, 0); bitcpy(a, 0, p, o->field.off, o->field.len); } popamlerror(); free(p); return; } /* * Read all field bits into a or write all of them from a, * preverve old bits in source if needed. */ static void fieldio(Aobj *o, void *a, int iswr) { uchar *p; int nbytes, mask; uintptr saddr, eaddr; doprint("field%s [%O] %#p\n", iswr ? "out" : "in", o, a); if(o->field.idx != nil){ idxfieldio(o, a, iswr); return; } if(o->field.bank != nil) fieldwr(o->field.bank, o->field.bankval); /* compute addresses relative to the region */ mask = o->field.accsz/8 - 1; saddr = o->field.off/8; /* bytes */ saddr &= ~mask; eaddr = (o->field.off + o->field.len + 7) / 8; /* bytes */ eaddr = (eaddr + mask) & ~mask; nbytes = eaddr - saddr; p = amlmalloc(nbytes); if(wasamlerror()){ free(p); amlerror(nil); } if(iswr && (o->field.off != saddr*8 || o->field.len != nbytes*8)) switch(o->field.update){ case AFwrones: memset(p, ~0, nbytes); break; case AFpreserve: regio(o->field.reg, p, saddr, nbytes, o->field.accsz/8, 0); break; } if(iswr){ bitcpy(p, o->field.off - saddr*8, a, 0, o->field.len); regio(o->field.reg, p, saddr, nbytes, o->field.accsz/8, 1); }else{ regio(o->field.reg, p, saddr, nbytes, o->field.accsz/8, 0); bitcpy(a, 0, p, o->field.off - saddr*8, o->field.len); } popamlerror(); free(p); } /* * read from a field into a buffer object. */ static void fieldrd(Aobj *fo, Aobj *o) { uchar *p; dxprint("fieldrd %O %O\n", fo, o); amltcheck(fo, Ofield, 0); amltcheck(o, Obuf, 0); if(o->buf.len * 8 < fo->field.len){ p = amlmalloc((fo->field.len+7)/8); if(wasamlerror()){ free(p); amlerror(nil); } fieldio(fo, p, 0); memmove(o->buf.p, p, o->buf.len); popamlerror(); free(p); }else{ memset(o->buf.p, 0, o->buf.len); fieldio(fo, o->buf.p, 0); } } /* * write o into a field */ static void fieldwr(Aobj *fo, Aobj *o) { Aobj *no; char *s; void *p; int n, fbytes, tot, left; dxprint("fieldwr %O %O\n", fo, o); amltcheck(fo, Ofield, 0); p = nil; no = nil; if(wasamlerror()){ amlclose(no); free(p); amlerror(nil); } fbytes = (fo->field.len+7)/8; switch(o->type){ case Ostr: case Odecstr: /* write each character as an integer value */ no = amlnewi(0); for(s = o->sval; *s != 0; s++){ no->ival = *s; fieldwr(fo, no); } break; case Oint: /* write bits from it, truncating or zero extending */ if(fbytes > acpisz) p = amlmalloc(fbytes); else p = amlmalloc(acpisz); memmove(p, &o->ival, acpisz); fieldio(fo, p, 1); break; case Obuf: /* write from o->buf.p fo->len bits at a time */ p = amlmalloc(fbytes); tot = 0; for(left = o->buf.len * 8; left > 0; left -= n){ n = fo->field.len; if(left < n) n = left; bitcpy(p, 0, o->buf.p, tot, n); fieldio(fo, p, 1); memset(p, 0, fbytes); tot += n; } break; default: no = tcast(o, Obuf); fieldwr(fo, no); break; } popamlerror(); amlclose(no); free(p); } /* * copy nbits bits from s (offset sbit bits) to a slice object. */ static void slicecpy(Aobj *o, void *s, int sbit, int nbits) { dxprint("slicecpy: s %#p off %#x:%d sz %#x -> [%O]\n", s, sbit/8, sbit&7, nbits, o); amltcheck(o, Oslice, 0); assert(o->slice.src != nil && o->slice.src->type == Obuf); bitcpy(o->slice.src->buf.p, o->slice.off, s, sbit, nbits); } /* * Type cast tools. */ static char* inttostr(u64int ival, int type) { char buf[15]; char *fmt[2] = { "%#08ux", "%#016ullx" }; char *s; if(type == Odecstr){ fmt[0] = "%08ud"; fmt[1] = "%016ud"; } if(amlenv->table->is64 == 0) seprint(buf, buf+sizeof(buf), fmt[0], (u32int)ival); else seprint(buf, buf+sizeof(buf), fmt[1], ival); s = nil; kstrdup(&s, buf); return s; } static u64int buftoint(uchar *p, int len) { u64int ival; int i; ival = 0; if(acpisz < len) len = acpisz; for(i = 0; i < len; i++) ival |= p[i] << i*8; return ival; } static u64int strtoint(char *s, int type) { int n, c, i; u64int ival; ival = 0; n = acpisz * 2; for(i = 0; i < n; i++){ if(s[i] >= '0' && s[i] <= '9') c = s[i]- '0'; else if(s[i] >= 'a' && s[i] <= 'f') c = s[i] - 'a' + 10; else if(s[i] >= 'A' && s[i] <= 'F') c = s[i] - 'A' + 10; else break; if(type == Ostr) ival = (ival << 4) | c; else ival = ival * 10 + c; } return ival; } static Aobj* toint(Aobj *o) { Aobj *no, *bo; switch(o->type){ case Ohandle: no = amlnewi(o->ival); break; case Ostr: case Odecstr: no = amlnewi(strtoint(o->sval, o->type)); break; case Obuf: no = amlnewi(buftoint(o->buf.p, o->buf.len)); break; default: bo = tobuf(o); no = toint(bo); amlclose(bo); break; } return no; } static Aobj* tobuf(Aobj *o) { Aobj *no, *so; void *p; int len; len = 0; p = nil; no = amlnew(Obuf); switch(o->type){ case Oslice: so = o->slice.src; no->buf.p = amlmalloc(so->buf.len); bitcpy(no->buf.p, 0, so->buf.p, o->slice.off, o->slice.len); no->buf.len = o->slice.len >> 3; if(o->slice.len & 7) no->buf.len++; return no; case Ofield: len = o->field.len >> 3; if(o->field.len & 7) len++; no->buf.p = amlmalloc(len); no->buf.len = len; fieldrd(o, no); return no; case Oint: case Ohandle: p = &o->ival; len = acpisz; break; case Ostr: case Odecstr: p = o->sval; len = strlen(o->sval) + 1; if(len == 1) len = 0; break; default: amlclose(no); amlerror("cast %s -> buf not supported", amltname(o->type)); } no->buf.len = len; no->buf.p = amlmalloc(len); if(len > 0) memmove(no->buf.p, p, len); return no; } static Aobj* tostr(Aobj *o, int type) { Aobj *no, *bo; char *s, *se; int i; char *fmt; fmt = "%#02ux"; if(type == Odecstr) fmt = "%ud"; no = amlnew(type); switch(o->type){ case Ohandle: case Oint: no->sval = inttostr(o->ival, type); break; default: if(wasamlerror()){ amlclose(no); amlerror("cast %s -> str not supported", amltname(o->type)); } bo = tobuf(o); popamlerror(); if(bo->buf.len == 0) kstrdup(&no->sval, ""); else if((o->type == Oslice || o->type == Ofield) && bo->buf.len <= acpisz){ /* must handle as integer */ no->sval = inttostr(buftoint(bo->buf.p, bo->buf.len), Ostr); }else{ s = no->sval = amlmalloc(bo->buf.len * 4 + 1); se = s + bo->buf.len * 4 + 1; for(i = 0; i < bo->buf.len; i++){ s = seprint(s, se, fmt, bo->buf.p[i]); if(i < bo->buf.len - 1) s = seprint(s, se, ","); } } amlclose(bo); } return no; } static Aobj* duplist(Aobj *l) { Aobj *nl, *r, **p; if(l == nil) return nil; r = nl = amlnew(l->type); dupobj(nl, l); p = &nl->next; for(; l != nil; l = l->next){ nl = amlnew(Onone); dupobj(nl, l); *p = nl; p = &nl->next; } return r; } /* * only memory copying and field I/O. * building block for amlcpy. */ static Aobj* dupobj(Aobj* o, Aobj *so) { Aobjhdr hdr; /* * args and locals must always keep their types. */ assert(o->type != Oarg && o->type != Olocal); /* * we copy field and slices always from buffers */ if(o->type == Oslice){ amltcheck(so, Obuf, 0); slicecpy(o, so->buf.p, 0, so->buf.len * 8); return o; } if(o->type == Ofield){ fieldwr(o, so); return o; } hdr = o->Aobjhdr; amlclean(o); memmove(o, so, sizeof(Aobj)); o->Aobjhdr = hdr; switch(so->type){ case Onone: case Oint: case Oscope: case Omethod: case Odev: case Opwr: case Oproc: case Othermal: case Ohandle: case Oreg: break; case Opkg: o->list.hd = duplist(o->list.hd); break; case Oref: if(o->oref.o != nil) incref(o->oref.o); break; case Ostr: case Odecstr: case Oname: o->sval = nil; kstrdup(&o->sval, so->sval); break; case Obuf: o->buf.p = amlmalloc(o->buf.len); memmove(o->buf.p, so->buf.p, o->buf.len); break; case Omutex: memset(&o->mutex, 0, sizeof(Nmutex)); break; case Oevent: memset(&o->event, 0, sizeof(Nevent)); break; default: panic("dupobj: unknown type %s\n", amltname(o->type)); } if(o->type == Onone) o->type = so->type; return o; } /* * return the object we should read to copy from it or to * build references to it (does not add a new ref). */ Aobj* amlread(Aobj *o) { Aname *nm; switch(o->type){ case Oarg: o = o->oref.o; if(o == nil) amlerror("read from null argument"); if(o->type == Oref) o = o->oref.o; break; case Olocal: o = o->oref.o; if(o == nil) amlerror("read from null local"); break; case Oname: nm = amlwalk(o->sval, Walkns, nil); if(nm == nil) amlerror("read from unbound name %s", o->sval); o = nm->o; break; } if(o == nil) amlerror("read from null object"); return o; } static Aobj* tcast(Aobj *o, int type) { if(o->type == type || type == Onone || type == Oany){ incref(o); return o; } dxprint("cast %s <- %s %O\n", amltname(type), amltname(o->type), o); switch(type){ case Ostr: case Odecstr: o = tostr(o, type); break; case Obuf: o = tobuf(o); break; case Oint: o = toint(o); break; default: o = nil; amlerror("tcast: cast to %s not supported", amltname(type)); } dxprint("cast %s -> %O\n", amltname(type), o); return o; } /* * Copy object so into o, perhaps doing a type conversion * if castok. It's ok if o is nil (may be a null target). * Copy must follow requirements regarding how to copy * to local objects and to argument objects. See 18.2.5.8. */ void amlcpy(Aobj *o, Aobj *so, int castok) { int dtype; Aobj *no, *to; Aname *nm; if(o == nil) return; dxprint("amlcpy [%O] <- [%O] %s\n", o, so, castok ? "castok" : ""); to = no = nil; if(so->type == Ofield || so->type == Oslice) to = so = tcast(so, Obuf); if(wasamlerror()){ amlclose(to); amlclose(no); amlerror(nil); } switch(o->type){ case Oarg: no = o->oref.o; if(no != nil && no->type == Oref && no->oref.o != nil) o = no->oref.o; else{ incref(so); popamlerror(); amlclose(o->oref.o); o->oref.o = so; amlclose(to); return; } break; case Olocal: popamlerror(); amlclose(o->oref.o); o->oref.o = amlnew(so->type); dupobj(o->oref.o, so); amlclose(to); dxprint("amlcpy [%O]\n", o); return; case Oname: nm = amlwalk(o->sval, Walkns, nil); if(nm == nil) amlerror("copy to unbound name %s", o->sval); o = nm->o; break; } if(castok && o->type != Ofield){ dtype = o->type; if(dtype == Oslice) dtype = Obuf; }else dtype = so->type; so = amlread(so); no = nil; if(dtype != so->type) so = no = tcast(so, dtype); dupobj(o, so); popamlerror(); amlclose(no); amlclose(to); } /* * Evaluate an object trying to get something of the type wanted. */ Aobj* amlcast(Aobj *o, int twanted) { Aname *nm; Aobj *no, *ro; if(o == nil) return nil; incref(o); ro = o; if(wasamlerror()){ amlclose(ro); amlerror(nil); } while(ro != nil && ro->type != twanted){ switch(ro->type){ case Olocal: case Oarg: case Oref: no = ro->oref.o; if(no != nil) incref(no); break; case Oname: nm = amlwalk(ro->sval, Walkns, nil); if(nm == nil){ amlwarn("cast to %s: '%s' not found in '%s'\n", amltname(twanted), amlenv->dot->s, ro->sval); amlclose(ro); ro = nil; goto Found; } no = nm->o; if(no != nil) incref(no); break; case Omethod: no = opcall(ro, nil); break; default: if(twanted == Oany) goto Found; no = tcast(ro, twanted); } amlclose(ro); ro = no; } if(ro == nil || (twanted != Oany && ro->type != twanted)) if((amlenv->flags&Enoeval) == 0) amlerror("no such object"); else amlwarn("objeval: null result\n"); Found: popamlerror(); return ro; } p); } /* * read from a field into a buffer object. */ static void fieldrd(Aobj *fo, Aobj *o) { uchar *p; dxprint("fieldrd %O %O\n", fo, o); amltcheck(fo, Ofi9am/amlop.c 644 0 0 141263 11326662515 10702ustar00nemosys/* * Main driver for AML operations. * * On most of them, the comment describes which * args are taken and which type of result is produced, if any. * Syntax is the same used for argument strings. */ #include "kernel.h" #include "acpi.h" #include "aml.h" /* Op flags */ enum { Dry = 1, /* call op on dry runs */ Bdy = 2, /* print { } around body */ }; static Aobj* pamlobj(void); static Aobj** checkargs(char*); static Op* decode(uchar* ps, uchar *pe, int *icp); static void opintprefix(int sz) { int i; amlenv->res = amlnew(Oint); for(i = 0; i < sz; i += 8) amlenv->res->ival |= amlnext() << i; amlnext(); } /* -> I */ static void opbyteprefix(void) { opintprefix(8); } /* * DANGER: prev is to be used only when we want to call an operation * just to decode its arguments and we are faking the instruction. * We step back so that the op call will skip the fake op and * start with the first byte of the arguments. */ static void prev(void) { amlenv->ps--; } /* process arg 'b' */ static Aobj* pbyte(void) { prev(); opintprefix(8); return amlenv->res; } /* -> I */ static void opwordprefix(void) { opintprefix(16); } /* process arg 'w' */ static Aobj* pword(void) { prev(); opintprefix(16); return amlenv->res; } /* -> I */ static void opdwordprefix(void) { opintprefix(32); } /* process arg 'd' */ static Aobj* pdword(void) { prev(); opintprefix(32); return amlenv->res; } /* -> I */ static void opqwordprefix(void) { opintprefix(64); } /* -> S */ static void opstrprefix(void) { Aobj *o; int l; for(l = 0; amlenv->ps[l] != 0; l++) if(amlenv->ps == amlenv->pe) amlerror("short string"); o = amlenv->res = amlnew(Ostr); o->sval = amlmalloc(l+1); if(l > 0) memmove(o->sval, amlenv->ps, l); o->sval[l] = 0; amlenv->ps += l+1; diprint("%!'%s'\n", o->sval); /* see the value */ } /* -> O (it's Olocal) */ static void oplocal(void) { int n; Mframe *mp; n = amlenv->ps[0] - OpLocal0; amlnext(); mp = amlenv->mp; if(mp == nil) panic("oplocal outside a call"); if(n < 0 || n >= Nmlcls) amlerror("bad local number"); if(mp->lcls[n] == nil){ if(mp->nlcls < n+1) mp->nlcls = n+1; mp->lcls[n] = amlnew(Olocal); mp->lcls[n]->oref.no = n; } incref(mp->lcls[n]); amlenv->res = mp->lcls[n]; } /* -> O (it's Oarg) */ static void oparg(void) { int n; Mframe *mp; n = amlenv->ps[0] - OpArg0; amlnext(); mp = amlenv->mp; if(mp == nil) panic("oparg outside a call"); if(n < 0 || n >= mp->nargs) amlerror("bad arg number"); if(mp->args[n] == nil){ mp->args[n] = amlnew(Oarg); mp->args[n]->oref.no = n; } incref(mp->args[n]); amlenv->res = mp->args[n]; } /* * -> N * * Args and locals can be used as names in the grammar. * It's called a supername. sic. * This operation encapsulates this, and returns a * local or an argument when it's a supername and not * a name what we want. */ static Aobj* pnamechar(void) { Aobj *o; int nels; int i; char prefix[50]; char *s; if(amlenv->ps[0] >= OpArg0 && amlenv->ps[0] <= OpArg6){ oparg(); return amlenv->res; } if(amlenv->ps[0] >= OpLocal0 && amlenv->ps[0] <= OpLocal7){ oplocal(); return amlenv->res; } o = amlenv->res = amlnew(Oname); s = prefix; *s = 0; if(amlenv->ps[0] == '\\'){ *s++ = amlenv->ps[0]; amlnext(); } while(amlenv->ps[0] == '^'){ *s++ = '^'; amlnext(); } *s = 0; switch(amlenv->ps[0]){ case OpDnameprefix: nels = 2; amlnext(); break; case OpMnameprefix: nels = amlnext(); amlnext(); break; case 0: kstrdup(&o->sval, ""); amlnext(); return o; default: if((amlenv->ps[0] >= 'A' && amlenv->ps[0] <= 'Z') || amlenv->ps[0] == '_') nels = 1; else{ amlclose(amlenv->res); return amlenv->res = nil; } break; } o->sval = amlmalloc(sizeof(prefix)+nels*5+1); strcpy(o->sval, prefix); s = o->sval + (s-prefix); for(i = 0; i < nels; i++){ memmove(&s[i*5], amlenv->ps, 4); if(i+1 < nels) s[i*5+4] = '.'; else s[i*5+4] = 0; amlenv->ps += 4; } return o; } /* * -> n */ static void opnamechar(void) { Aobj *o; Aname *n; o = amlenv->res = pnamechar(); if(o == nil || o->sval == nil) amlerror("bad name"); if(amlenv->flags&(Enocall)){ diprint("%!\"%s\"\n", o->sval); return; } n = amlwalk(o->sval, Walkns, nil); if(n == nil || n->o == nil){ diprint("%!\"%s\"\n", o->sval); if((amlenv->flags&Enoeval) == 0) amlerror("name %s not found or bound to nil\n", o->sval); else amlwarn("name %s not found or bound to nil\n", o->sval); return; } amlenv->res = nil; if(n->o->type != Omethod){ diprint("%!\"%s\"\n", o->sval); incref(n->o); amlclose(o); amlenv->res = n->o; }else{ amlclose(o); amlenv->res = opcall(n->o, nil); } } /* * -> I */ static void opnum(void) { Aobj *o; o = amlenv->res = amlnew(Oint); switch(amlenv->ic){ case OpZero: case OpOne: o->ival = amlenv->ic; break; case OpOnes: o->ival = ~0; break; case OpTimer: case OpDebug: o->ival = perfticks(); break; case OpRevision: o->ival = 9; break; default: panic("opnum bug"); } } /* * name:n obj:O */ static void opname(void) { Aobj **args; args = checkargs("nO"); amlbind(args[0]->sval, args[1]); } /* * new name for object. * srcobj:O name:n */ static void opalias(void) { Aobj **args; args = checkargs("On"); amlbind(args[1]->sval, args[0]); } /* * name:n */ static void opevent(void) { Aobj **args; args = checkargs("n"); amlenv->res = amlnew(Oevent); amlbind(args[0]->sval, amlenv->res); } /* * name:n flags:b */ static void opmutex(void) { Aobj **args, *o; args = checkargs("nb"); o = amlenv->res = amlnew(Omutex); o->mutex.flags = args[1]->ival; amlbind(args[0]->sval, amlenv->res); } /* * named region in memory, I/O, etc. * name:n space:b base:I len:I */ static void opopregion(void) { Aobj **args, *o; args = checkargs("nbII"); o = amlenv->res = amlnew(Oreg); o->reg.spc = args[1]->ival; o->reg.base = args[2]->ival; o->reg.len = args[3]->ival; amlbind(args[0]->sval, o); } /* * len sz:I data -> B */ static void opbuf(void) { Aobj **args, *o; int n, sz; args = checkargs("I"); if(args[0]->ival > 64 * 1024) amlerror("bad buffer sz %lld", args[0]->ival); o = amlenv->res = amlnew(Obuf); sz = o->buf.len = args[0]->ival; o->buf.p = amlmalloc(sz+1); n = amlenv->argpe - amlenv->ps; if(n > sz) n = sz; if(n > 0) memmove(o->buf.p, amlenv->ps, n); if(sz > n) memset(o->buf.p + n, 0, sz - n); amlenv->ps = amlenv->argpe; } /* * It's an array, we use a list. * package: len nitems:b ... -> P * varpackage: pkglen nitems:I ... -> P */ static void oppackage(void) { Aobj **args, *o, **ol; Env env2; args = checkargs("I"); if(args[0]->ival > 256) amlerror("package with more than 256 objects"); o = amlenv->res = amlnew(Opkg); o->list.n = args[0]->ival; amlpush(&env2, amlenv->op->name, nil, amlenv->argpe); env2.flags |= Enocall; /* Ignore errors and try to get as many elements as * we can. Users may always check out if they got what * they need. * We may return more elements than declared upon errors, * but that should not harm and it's easier. */ if(!wasamlerror()){ pamlblock(Keep); popamlerror(); } o->list.hd = amlenv->olist.hd; if(o->list.n > amlenv->olist.n){ for(ol = &o->list.hd; *ol != nil; ol = &(*ol)->next) ; while(o->list.n > amlenv->olist.n){ *ol = amlnewi(0); ol = &(*ol)->next; amlenv->olist.n++; } } amlenv->olist.hd = nil; amlenv->olist.n = 0; amlpop(); } static void setfieldflags(Aobj *o, uint ival) { o->field.accsz = ival & 0xF; switch(o->field.accsz){ default: o->field.accsz = 8; break; case AFword: o->field.accsz = 16; break; case AFdword: o->field.accsz = 32; break; case AFqword: o->field.accsz = 64; break; } o->field.locking = ival>>4 & 1; o->field.update = ival>>5 & 3; } /* * Create a bit field for a region. * field: len reg:R flags:b fields:f */ static void opfield(void) { Aobj *ol, **args; args = checkargs("Rbf"); for(ol = args[2]->list.hd; ol != nil; ol = ol->next){ amltcheck(ol, Ofield, 1); incref(args[0]); assert(ol->field.reg == nil); ol->field.reg = args[0]; setfieldflags(ol, args[1]->ival); if(ol->field.fname != nil) amlbind(ol->field.fname, ol); else amlwarn("field with no name %O\n", ol); } } /* * Create bit field for a banked register. * len reg:R bankname:n bankval:I flags:b fields:f */ static void opbankfield(void) { Aobj *ol, **args; char *bname; Aname *n; args = checkargs("RnIbf"); for(ol = args[4]->list.hd; ol != nil; ol = ol->next){ amltcheck(ol, Ofield, 1); incref(args[0]); ol->field.reg = args[0]; bname = args[1]->sval; n = amlwalk(bname, Walkns, nil); if(n != nil && n->o != nil && n->o->type == Ofield){ incref(n->o); ol->field.bank = n->o; } ol->field.bankval = amlnewi(args[2]->ival); setfieldflags(ol, args[3]->ival); if(ol->field.fname == nil) amlwarn("bankfield with no name %O\n", ol); else if(ol->field.bank == nil) amlwarn("bankfield with no bank '%s' %O\n", bname, ol); else amlbind(ol->field.fname, ol); } } /* * Create a bit field within a data object indexed by a value. * len idx:F data:F flags:b fields:f */ static void opindexfield(void) { Aobj *ol, **args; args = checkargs("FFbf"); for(ol = args[3]->list.hd; ol != nil; ol = ol->next){ amltcheck(ol, Ofield, 1); /* adjust offset back to bytes (value for index) */ incref(args[0]); ol->field.idx = args[0]; ol->field.idxval = amlnewi(ol->field.off >> 3); ol->field.off &= 7; incref(args[1]); ol->field.data = args[1]; setfieldflags(ol, args[2]->ival); if(ol->field.fname != nil) amlbind(ol->field.fname, ol); else amlwarn("indexfield with no name %O\n", ol); } } /* * Create a slice of a buffer. * We always use bit offsets/lengths, to keep all of them homogeneous. * mkfield: buf:B off:I len:I name:n * mk{bit,byte,word,dword,qword}field: buf:B off:I name:n */ static void opmkxfield(void) { Aobj **args, *o, *no; if(amlenv->ic == OpMkfield){ args = checkargs("BIIn"); no = args[3]; }else{ args = checkargs("BIn"); no = args[2]; } o = amlenv->res = amlnew(Oslice); incref(args[0]); o->slice.src = args[0]; switch(amlenv->ic){ case OpMkfield: o->slice.off = args[1]->ival; o->slice.len = args[2]->ival; break; case OpMkbitfield: o->slice.off = args[1]->ival; o->slice.len = 1; break; case OpMkbytefield: o->slice.off = args[1]->ival<<3; o->slice.len = 8; break; case OpMkwordfield: o->slice.off = args[1]->ival<<3; o->slice.len = 16; break; case OpMkdwordfield: o->slice.off = args[1]->ival<<3; o->slice.len = 32; break; case OpMkqwordfield: o->slice.off = args[1]->ival<<3; o->slice.len = 64; break; default: panic("opmkxfield bug"); } amlbind(no->sval, o); } Aobj* nth(Aobj *o, int n) { Aobj *l; for(l = o->list.hd; l != nil; l = l->next) if(n-- == 0) return l; return nil; } /* * reference to ith object in another object. * bufstrpkg:O idx:I res:t -> Oref */ static void opindex(void) { Aobj **args, *o, *no; args = checkargs("OIt"); o = amlenv->res = amlnew(Oref); /* * return ref to idx-th object within O */ switch(args[0]->type){ case Opkg: no = o->oref.o = nth(args[0], args[1]->ival); if(no != nil) incref(no); break; case Oint: case Ostr: case Odecstr: case Obuf: no = o->oref.o = amlnew(Oslice); no->slice.src = amlcast(args[0], Obuf); no->slice.off = args[1]->ival << 3; no->slice.len = 8; break; default: amlerror("index on object of type %s", amltname(args[0]->type)); } amlcpy(args[2], o, Cast); } /* * Complex object with nested scope. * scope: len name:n ... * device: len name:n ... * method: len name:n flags:b ... * pwrrsrc: len name:n syslvl:b rorder:w ... * processor: len name:n id:b baddr:d blen:b ... * thermalzone: len name:n ... */ static void opscope(void) { Aobj *o, **args; Env env2; char *name, *nval; Aname *dot; int ic; name = amlenv->op->name; args = checkargs("n"); nval = args[0]->sval; ic = amlenv->ic; switch(ic){ case OpMethod: o = amlenv->res = amlnew(Omethod); if((amlenv->flags&Enoeval) == 0){ amltcheck(args[1], Oint, 0); kstrdup(&o->method.name, nval); o->method.isexcl = args[1]->ival & 8; o->method.synclvl = (args[1]->ival>>4) & 0xF; o->method.nargs = args[1]->ival & 7; o->method.ps = amlenv->ps; o->method.pe = amlenv->argpe; } /* methods are not evaluated now */ amlenv->ps = amlenv->argpe; if((amlenv->flags&Enoeval) == 0) amlbind(o->method.name, o); return; case OpScope: o = amlenv->res = amlnew(Oscope); if(amlenv->flags&Enoeval) break; o->scope.pe = amlenv->argpe; break; case OpDevice: o = amlenv->res = amlnew(Odev); o->dev.pe = amlenv->argpe; break; case OpPwrrsrc: o = amlenv->res = amlnew(Opwr); if(amlenv->flags&Enoeval) break; amltcheck(args[1], Oint, 0); amltcheck(args[2], Oint, 0); o->pwr.syslvl = args[1]->ival; o->pwr.rorder = args[2]->ival; break; case OpProcessor: o = amlenv->res = amlnew(Oproc); if(amlenv->flags&Enoeval) break; amltcheck(args[1], Oint, 0); amltcheck(args[2], Oint, 0); amltcheck(args[3], Oint, 0); o->proc.id = args[1]->ival; o->proc.baddr = args[2]->ival; o->proc.blen = args[3]->ival; break; case OpThermalzone: o = amlenv->res = amlnew(Othermal); break; default: SET(o); panic("opscope bug"); } if((amlenv->flags&Enoeval) == 0){ dot = amlwalk(nval, Walkcreating, nil); if(dot == nil) amlerror("%s: can't walk to %s", name, nval); }else dot = amlenv->dot; amlpush(&env2, amlenv->op->name, nil, amlenv->argpe); amlenv->dot = dot; if(ic == OpMethod) env2.flags |= Enoeval; /* dump only */ if(wasamlerror()){ amlpop(); amlerror(nil); } pamlblock(Dontkeep); if((amlenv->flags&Enoeval) == 0 && ic != OpScope){ amlenv->dot = amlenv->prev->dot; amlbind(nval, o); } popamlerror(); amlpop(); } /* * tobuf: arg:B res:t ->b * todecstr: arg:D res:t -> S * tohexstr: arg:S res:t ->S * toint: arg:I res:t ->I * copyobject: arg:O res:t ->O * store: arg:O res:t ->O */ static void opcast(void) { Aobj **args; int docast; args = checkargs(amlenv->op->args); incref(args[0]); amlenv->res = args[0]; docast = amlenv->ic != OpCopyobject; amlcpy(args[1], args[0], docast); } /* * arg:B len:I res:t -> S */ static void optostr(void) { Aobj *o, **args; int n; args = checkargs("BIt"); n = args[1]->ival; if(n > args[0]->buf.len) n = args[0]->buf.len; o = amlenv->res = amlnew(Ostr); o->sval = amlmalloc(n + 1); if(n > 0) memmove(o->sval, args[0]->buf.p, n); o->sval[n] = 0; /* in case the buffer does to have a '\0' */ amlcpy(args[2], o, Cast); } /* * inc: arg:I -> I * dec: arg:I -> I */ static void opincdec(void) { Aobj **args; args = checkargs("I"); dxprint("%s %O\n", amlenv->op->name, args[0]); if(amlenv->ic == OpInc) args[0]->ival++; else args[0]->ival--; amlenv->res = amlnewi(args[0]->ival); } static int objtype(Aobj *o) { while(o != nil && (o->type == Oref || o->type == Oarg || o->type == Olocal)) o = o->oref.o; if(o == nil) return Tnone; switch(o->type){ case Oslice: return Tslice; case Ofield: return Tfield; case Omethod: return Tmethod; case Odev: return Tdev; case Oproc: return Tproc; case Opwr: return Tpwr; case Othermal: return Tthermal; case Ohandle: return Thandle; case Obuf: return Tbuf; case Oevent: return Tevent; case Oint: return Tint; case Omutex: return Tmutex; case Opkg: return Tpkg; case Oreg: return Treg; case Odecstr: case Ostr: return Tstr; case Oname: /* don't want to resolve the name */ /* leave it so by now */ default: return Tnone; } } static long objsize(Aobj *o) { switch(o->type){ case Ostr: case Odecstr: return strlen(o->sval); case Obuf: return o->buf.len; case Opkg: return o->list.n; default: amlerror("not a buffer, string, or package"); } return 0; } /* * objecttype: arg:O -> I * sizeof: arg:O -> I */ static void opunaryobj(void) { Aobj *o, **args; args = checkargs("O"); o = amlenv->res = amlnew(Oint); switch(amlenv->ic){ case OpObjecttype: o->ival = objtype(args[0]); break; case OpSizeof: o->ival = objsize(args[0]); break; default: amlerror("aml opunary bug"); } } /* * term:I term:I -> I */ static void oplandor(void) { Aobj **args; args = checkargs("II"); if(amlenv->ic == OpLand) amlenv->res = amlnewi(args[0]->ival && args[1]->ival); else amlenv->res = amlnewi(args[0]->ival || args[1]->ival); } /* * lnot: term:I -> I */ static void oplnot(void) { Aobj **args; args = checkargs("I"); amlenv->res = amlnewi(args[0]->ival == 0); } /* * Inefficient, as everything else; and not compliant. but should suffice. * lequal, lgreater, lless: term:B term:B -> I */ static void oplcmp(void) { Aobj **args, *o, *arg0, *arg1; int len, r; int isint; args = checkargs("OO"); arg1 = arg0 = nil; isint = args[0]->type == Oint && args[1]->type == Oint; if(wasamlerror()){ amlclose(arg0); amlclose(arg1); amlerror(nil); } r = 0; if(!isint){ arg0 = amlcast(args[0], Obuf); arg1 = amlcast(args[1], Obuf); len = arg0->buf.len; if(len > arg1->buf.len) len = arg1->buf.len; r = memcmp(arg0->buf.p, arg1->buf.p, len); } o = amlenv->res = amlnew(Oint); switch(amlenv->ic){ case OpLequal: if(isint) o->ival = args[0]->ival == args[1]->ival; else o->ival = (r == 0 && arg0->buf.len == arg1->buf.len); break; case OpLgreater: if(isint) o->ival = args[0]->ival > args[1]->ival; else{ o->ival = r > 0; if(r == 0 && arg0->buf.len > arg1->buf.len) o->ival = 1; } break; case OpLless: if(isint) o->ival = args[0]->ival < args[1]->ival; else{ o->ival = r < 0; if(r == 0 && arg0->buf.len < arg1->buf.len) o->ival = 1; } break; default: panic("oplcmp bug"); } popamlerror(); amlclose(arg0); amlclose(arg1); } static int msb(u64int n) { int r; if(n == 0) return 0; for(r = 1; n != 1; r++) n >>= 1; return r; } static int lsb(u64int n) { int r; if(n == 0) return 0; for(r = 1; (n & 1) == 0; r++) n >>= 1; return r; } static uvlong baseconv(uvlong v, int ibase, int obase) { uvlong r, p; r = 0; p = 1; while(v != 0){ r += (v % ibase) * p; v /= ibase; p *= obase; } return r; } static uvlong frombcd(uvlong v) { return baseconv(v, 16, 10); } static uvlong tobcd(uvlong v) { return baseconv(v, 10, 16); } /* * arg:I res:t -> I */ static void opunaryt(void) { Aobj *o, **args; args = checkargs("It"); o = amlenv->res = amlnew(Oint); switch(amlenv->ic){ case OpLeftsetbit: o->ival = msb(args[0]->ival); break; case OpRightsetbit: o->ival = lsb(args[0]->ival); break; case OpFrombcd: o->ival = frombcd(args[0]->ival); break; case OpTobcd: o->ival = tobcd(args[0]->ival); break; case OpNot: o->ival = ~args[0]->ival; break; default: panic("opunaryt bug"); } amlcpy(args[1], o, Cast); } /* * term:I term:I res:t -> I */ static void opbinaryt(void) { Aobj *o, **args; args = checkargs("IIt"); o = amlenv->res = amlnew(Oint); switch(amlenv->ic){ case OpAdd: o->ival = args[0]->ival + args[1]->ival; break; case OpSub: o->ival = args[0]->ival - args[1]->ival; break; case OpMultiply: o->ival = args[0]->ival * args[1]->ival; break; case OpMod: if(args[1]->ival == 0) amlerror("divide by zero"); o->ival = args[0]->ival % args[1]->ival; break; case OpAnd: o->ival = args[0]->ival & args[1]->ival; break; case OpOr: o->ival = args[0]->ival | args[1]->ival; break; case OpNor: o->ival = ~(args[0]->ival | args[1]->ival); break; case OpNand: o->ival = ~(args[0]->ival & args[1]->ival); break; case OpXor: o->ival = args[0]->ival ^ args[1]->ival; break; case OpShl: o->ival = args[0]->ival << args[1]->ival; break; case OpShr: o->ival = args[0]->ival >> args[1]->ival; break; default: panic("opbinaryt bug"); } amlcpy(args[2], o, Cast); } /* * num:I den:I rem:t quot:t -> I */ static void opdivide(void) { Aobj *o, **args; args = checkargs("IItt"); o = amlenv->res = amlnew(Oint); if(args[1]->ival == 0) amlerror("divide by zero"); o->ival = args[0]->ival % args[1]->ival; amlcpy(args[2], o, Cast); o->ival = args[0]->ival / args[1]->ival; amlcpy(args[3], o, Cast); } /* * concat strings if arg1 is a string, or buffers otherwise. * arg1:O arg2:O res:t -> B|S */ static void opconcat(void) { Aobj **args, *o, *arg0, *arg1; args = checkargs("OOt"); arg1 = nil; if(args[0]->type == Ostr || args[0]->type == Odecstr){ incref(args[0]); arg0 = args[0]; }else arg0 = amlcast(args[0], Obuf); if(wasamlerror()){ amlclose(arg0); amlclose(arg1); amlerror(nil); } arg1 = amlcast(args[1], arg0->type); if(arg0->type == Obuf){ o = amlenv->res = amlnew(Obuf); o->buf.len = arg0->buf.len + arg1->buf.len; o->buf.p = amlmalloc(o->buf.len); memmove(o->buf.p, arg0->buf.p, arg0->buf.len); memmove(o->buf.p + arg0->buf.len, arg1->buf.p, arg1->buf.len); }else{ o = amlenv->res = amlnew(Ostr); o->sval = amlmalloc(strlen(arg0->sval) + strlen(arg1->sval) + 1); strcpy(o->sval, arg0->sval); strcat(o->sval, arg1->sval); } popamlerror(); amlclose(arg0); amlclose(arg1); amlcpy(args[2], o, Cast); } /* * return ACPI resource length not counting the end tag, if any. */ static int reslen(uchar *p, int n) { int tot, len; for(tot = 0; tot < n; tot += len){ if(p[tot] & 0x80) /* small resource */ len = 1 + (p[tot] & 3); else if(p[tot] != ARendtag && tot + 3 <= n) /* large resource and not the end tag */ len = 3 + l16get(&p[tot+1]); else break; } return tot; } /* * arg1:B arg2:B res:t -> B */ static void opconcatres(void) { Aobj **args, *o; int b0len, b1len; args = checkargs("BBt"); b0len = reslen(args[0]->buf.p, args[0]->buf.len); b1len = reslen(args[0]->buf.p, args[0]->buf.len); o = amlenv->res = amlnew(Obuf); o->buf.len = b0len + b1len + 2; o->buf.p = amlmalloc(o->buf.len); memmove(o->buf.p, args[0]->buf.p, b0len); memmove(o->buf.p + b0len, args[1]->buf.p, b1len); o->buf.p[b0len + b1len] = ARendtag; o->buf.p[b0len + b1len + 1] = ARdontcksum; amlcpy(args[2], o, Cast); } /* * obj:n ref:t -> I */ static void opcondrefof(void) { Aobj **args, *no, *ro; args = checkargs("nt"); if(wasamlerror()){ if(isamlbranch()) amlerror(nil); /* ignore errors and return false */ amlenv->res = amlnewi(0); return; } no = amlread(args[0]); /* check we can read it */ popamlerror(); ro = amlenv->res = amlnew(Oref); incref(no); ro->oref.o = no; amlcpy(args[1], ro, Nocast); amlclose(ro); amlenv->res = amlnewi(1); } /* * obj:o -> O */ static void oprefof(void) { Aobj **args, *o; args = checkargs("o"); o = amlenv->res = amlnew(Oref); o->oref.o = amlread(args[0]); incref(o->oref.o); } /* * arg:o -> O */ static void opderefof(void) { Aobj **args, *o; Aname *n; args = checkargs("o"); o = amlread(args[0]); switch(o->type){ case Ostr: case Odecstr: n = amlwalk(o->sval, Walkns, nil); if(n == nil) amlerror("name not found"); o = n->o; break; case Oref: o = o->oref.o; break; } incref(o); amlenv->res = o; } /* * src:O idx:I len:I res:t -> B|S */ static void opmid(void) { Aobj **args, *o, *arg0; void *sp, *dp; int len, off, n; args = checkargs("OIIt"); switch(args[0]->type){ case Ostr: case Odecstr: arg0 = nil; sp = args[0]->sval; n = strlen(args[0]->sval); o = amlenv->res = amlnew(Ostr); break; default: arg0 = amlcast(args[0], Obuf); o = amlenv->res = amlnew(Obuf); sp = arg0->buf.p; n = arg0->buf.len; break; } off = args[1]->ival; len = args[2]->ival; if(off + len > n) len = n - off; if(len > 0){ if(o->type == Obuf){ dp = o->buf.p = amlmalloc(len); o->buf.len = len; }else{ dp = o->sval = amlmalloc(len + 1); o->sval[len] = 0; } memmove(dp, sp, len); } amlclose(arg0); amlcpy(args[3], o, Cast); } static int matches(int op, uvlong a1, uvlong a2) { switch(op){ case Mtrue: return 1; case Meq: return a1 == a2; case Mle: return a1 <= a2; case Mlt: return a1 < a2; case Mge: return a1 >= a2; case Mgt: return a1 > a2; default: amlerror("matched: unknown op %d", op); } return 0; } /* * starting at idx in src search for item i s.t. * src[i] op1 arg1 and src[i] op2 arg2. * Return i or -1. * [0]src:P [1]op1:b [2]arg1:I [3]op2:b [4]arg2:I [5]idx:I -> I */ static void opmatch(void) { Aobj **args, *o, *po, *io; int i, r; args = checkargs("PbIbII"); o = amlenv->res = amlnewi(-1); po = args[0]->list.hd; for(i = 0; po != nil && i < args[5]->ival; i++) po = po->next; for(r = 0; r == 0 && po != nil; po = po->next){ io = amlcast(po, Oint); r = matches(args[1]->ival, io->ival, args[2]->ival) && matches(args[3]->ival, io->ival, args[4]->ival); amlclose(io); if(r != 0) o->ival = i; else i++; } } /* * search XSDT for sig, oemid and oemtblid * and load it using root scope. * Then copy paramv into parm and return dbhandle or 0 * [0]sig:S [1]oemid:S [2]oemtblid:S [3]root:S [4]param:S [5]paramv:B -> I */ static void oploadtable(void) { amlwarn("load table not implemented"); /* * found = acpixsdt(args[0]->sval, args[1]->sval, args[2]->sval); * t = findtable(args[0]->sval, args[1]->sval, args[2]->sval); * create env for acpins and use it to eval t->data. * copyobj(args[4], args[5], Cast); * return handle obj with t->id; */ amlenv->res = amlnewi(0); } /* * load SSDT found in region and store its handle. * reg:O handle:t */ static void opload(void) { Aobj **args, *o; Nreg *r; long off, len; off = len = 0; r = nil; args = checkargs("Ot"); switch(args[0]->type){ case Oreg: r = &args[0]->reg; len = r->len; break; case Ofield: if(args[0]->field.reg == nil) amlerror("null region"); r = &args[0]->field.reg->reg; off = args[0]->field.off >> 3; len = args[0]->field.len >> 3; break; default: amlerror("load requires region or field"); } if(r->spc != Rsysmem) amlerror("can load only from system memory"); if(r->p == nil) r->p = vmap(r->base, r->len); if(off + len > r->len) len = r->len - off; if(len < Sdthdrsz) amlerror("region is too small for load"); if(acpidsdt(r->p + off, len) < 0) amlerror("table load failed"); o = amlenv->res = amlnew(Ohandle); o->ival = -1; // (uintptr)t; amlcpy(args[1], o, Cast); amlerror("load not implemented"); } /* * unload SSDT named by handle. * handle:I */ static void opunload(void) { Aobj **args; args = checkargs("I"); dxprint("unload %O\n", args[0]); // closetable((Atable*)args[0]->ival); } /* * named memory region for an ACPI table. * name:n sig:S oem:S tbl:S */ static void opdataregion(void) { Aobj **args, *o; args = checkargs("nSSS"); o = amlenv->res = amlnew(Oreg); /* * t = findtable(args[1]->sval, args[2]->sval, args[3]->sval); * if(t == nil) * amlerror("no such table"); * o->reg.spc = Rsysmem; * o->reg.p = (uintptr)t->tbl; * o->reg.len = t->tbl->len; */ amlwarn("dataregion called but not really implemented"); amlbind(args[0]->sval, o); } static void opnop(void) { } /* * n:I */ static void opsleep(void) { Aobj **args; args = checkargs("I"); dxprint("delay %lld\n", args[0]->ival); switch(amlenv->ic){ case OpSleep: /* should use tsleep if up */ delay(args[0]->ival); /* ms */ break; case OpStall: /* can't use tsleep */ microdelay(args[0]->ival); /* µs */ break; default: panic("opsleep bug"); } } /* * obj:O ev:I */ static void opnotify(void) { Aobj **args; int ev; args = checkargs("OI"); dxprint("notify %O %O\n", args[0], args[1]); ev = args[1]->ival; switch(args[0]->type){ case Odev: args[0]->dev.handler(args[0], ev); break; case Oproc: args[0]->proc.handler(args[0], ev); break; case Othermal: args[0]->thermal.handler(args[0], ev); break; } } /* * The same invocation may re-acquire a mutex * it holds.The mutex can be the global lock. * * As long as we use a single proc to run the * interpreter, we can assume we can always acquire locks. * The first call for the global lock acquires it. * The last one releasing releases it. */ /* * mutex:M tmout:w -> I */ static void opacquire(void) { Aobj **args; args = checkargs("Mw"); dxprint("acquire %O\n", args[0]); args[0]->mutex.count++; if(args[0]->mutex.count == 1 && args[0] == acpilock) acpiglock(); amlenv->res = amlnewi(0); /* 0 == ok */ } /* * mutex:M */ static void oprelease(void) { Aobj **args; args = checkargs("M"); dxprint("release %O\n", args[0]); args[0]->mutex.count--; if(args[0]->mutex.count == 0 && args[0] == acpilock) acpigunlock(); } /* * event:E */ static void opreset(void) { Aobj **args; args = checkargs("E"); dxprint("reset %O\n", args[0]); args[0]->event.fire = 0; } /* * event:E */ static void opsignal(void) { Aobj **args; args = checkargs("E"); dxprint("signal %O\n", args[0]); args[0]->event.fire++; } /* * event:E tmout:I -> I */ static void opwait(void) { Aobj **args; args = checkargs("E"); dxprint("wait %O\n", args[0]); amlenv->res = amlnewi(0); do{ if(args[0]->event.fire) return; /* should wait for the event if up. * There may be many invocations calling wait * on the same event. */ }while(0); amlenv->res->ival = 1; } /* * type:b code:d arg:I */ static void opfatal(void) { Aobj **args; args = checkargs("bdI"); xprint("acpi: fatal(%lld, %lld, %lld)\n", args[0]->ival, args[1]->ival, args[2]->ival); amlerror("fatal"); } /* * We assume that a method won't bind a child and then its parent. */ static void unbindnames(Aname *n) { Aname *nn; for(; n != nil; n = nn){ nn = n->mnext; amlunbind(n); } } /* * call a method. used by opnamechar and amlcast. * If uargs is nil, args are parsed from aml code at current ps. */ Aobj* opcall(Aobj *o, Aobj **uargs) { Aobj *r; Mframe mfr; Env env2; int i, nargs; uchar *oldps; if(o->type != Omethod) panic("aml: call on non method"); nargs = o->method.nargs; memset(&mfr, 0, sizeof(mfr)); /* * 1. parse and evaluate arguments in caller's environment. * Keep them in targs so that stack schecks may find * those objects in the stack and errors raised know they * must be cleaned up. They are unused otherwise. */ amlpush(&env2, "call", nil, nil); env2.dot = o->name; if(uargs == nil && nargs > 0){ /* record temp. args, for stack checks and errors */ env2.mp->targs = mfr.args; if(wasamlerror()){ if(env2.mp->targs != nil) for(i = 0; i < nargs; i++){ amlclose(mfr.args[i]); mfr.args[i] = nil; } amlpop(); amlerror(nil); } diprint("%!call \"%s\"(\n", o->name->path); for(i = 0; i < nargs; i++){ mfr.args[i] = amlcast(pamlobj(), Oany); diprint("%!marg%d: %O\n", i, mfr.args[i]); amlclose(amlenv->res); amlenv->res = nil; } popamlerror(); diprint("%!)\n"); env2.mp->targs = nil; /* they are in mp->args now. */ }else{ for(i = 0; i < nargs && uargs[i] != nil; i++) mfr.args[i] = uargs[i]; nargs = i; for(; i < nelem(mfr.args); i++) mfr.args[i] = nil; } mfr.nargs = nargs; env2.mp = 𝔪 /* It's official now */ if(amlenv->flags&Enoeval){ amlpop(); incref(o); return o; } /* * 2. Call the method. */ if(o->method.trace > 0) amlenv->flags |= Exdump|Eidump; dxprint("call %O ps %#p\n", o, o->method.ps); if(o->method.trace < 0) amlenv->flags &= ~(Exdump|Eidump); oldps = amlenv->ps; r = nil; if(wasamlerror()){ assert(amlenv->mp == &mfr); if(strncmp(amlerrstk.msg, "return at", 9) == 0){ r = amlenv->res; amlenv->res = nil; goto Done; } unbindnames(mfr.binds); amlpop(); amlenv->ps = oldps; /* amlpop fails to set ps */ amlerror(nil); } env2.ps = o->method.ps; env2.pe = o->method.pe; if(o->method.f != nil) o->method.f(o); else pamlblock(Dontkeep); popamlerror(); Done: unbindnames(mfr.binds); amlpop(); amlenv->ps = oldps; /* amlpop fails to set ps */ amlclose(amlenv->res); amlenv->res = nil; if(r != nil) amlenv->res = r; dxprint("ret %s: %O\n", o->method.name, r); return amlenv->res; } /* * arg:O */ static void opreturn(void) { Aobj **args; Env *e; if((amlenv->flags&Enoeval) == 0){ args = checkargs("O"); /* leave the result in the environment that made the call */ for(e = amlenv; e->prev != nil && e->prev->mp == e->mp; e = e->prev) ; if(e->mp == nil) amlerror("return not in method call"); amlclose(e->res); incref(args[0]); e->res = args[0]; amlerror("return"); /* catched by opcall() */ } } /* * Both if and while. * len cond:I ... */ static void opif(void) { Env env2, *env; int loop; uchar *p0, *pe; char *name; checkargs("I"); env = amlenv; loop = env->ic == OpWhile; name = "if"; if(loop) name = "while"; p0 = env->opp0; pe = env->argpe; if((env->flags&Enoeval) == 0){ if(env->args[0]->ival != 0) env->flags |= Ewastrue; else env->flags &= ~Ewastrue; if((env->flags&Ewastrue) == 0){ env->ps = pe; /* do not evaluate anything now */ return; } } amlpush(&env2, name, nil, pe); if(wasamlerror()){ amlpop(); if(isamlbranch()){ if(loop == 0 || strncmp(amlerrstk.msg, "return", 6) == 0) amlerror(nil); if(strncmp(amlerrstk.msg, "continue", 8) == 0) goto Next; /* break the loop */ xprint("%s: error: %s\n", name, amlerrstk.msg); } return; } pamlblock(Dontkeep); amlpop(); popamlerror(); Next: if((env->flags&Enoeval) == 0 && loop != 0){ dxprint("loop\n"); env->ps = p0; /* pretend we are unparsed: loop */ } } /* * len ... */ static void opelse(void) { Env env2, *env; env = amlenv; if((env->flags&Enoeval) == 0 && (env->flags&Ewastrue) != 0){ env->ps = env->argpe; /* do not evaluate now */ return; } amlpush(&env2, "elsescope", nil, env->argpe); if(wasamlerror()){ amlpop(); amlerror(nil); } env->res = pamlblock(Dontkeep); amlpop(); popamlerror(); } /* * continue and break raise their own names, both cached by opif() * continue continues the innermost while (opif). * break breaks the innermost while (opif); */ static void opjump(void) { if((amlenv->flags&Enoeval) == 0){ dxprint("%s\n", amlenv->op->name); amlerror(amlenv->op->name); } } /* * Argument processing tools. * * Parsing functions below are delicate regarding * ref counting and env handling. That's the price for * keeping ops above more simple in that respect. */ static int ppkglen(void) { int len; len = amlenv->ps[0]; switch(len & 0xC0){ case 0x00: break; case 0x40: len &= 0xF; len |= amlnext() << 4; break; case 0x80: len &= 0xF; len |= amlnext() << 4; len |= amlnext() << 12; break; case 0xC0: len &= 0xF; len |= amlnext() << 4; len |= amlnext() << 12; len |= amlnext() << 20; break; default: amlerror("bad pkglen %#ux\n", len); } amlnext(); return len; } /* * Parse a field list argument and return Opkg with Ofields */ static Aobj* pfieldlist(void) { Aobj *lo; Aobj **ol; Aobj *fo; Aobj *n; Nfield f; int off; lo = amlnew(Opkg); ol = &lo->list.hd; if(wasamlerror()){ amlclose(lo); amlerror(nil); } off = 0; while(amlenv->ps < amlenv->pe){ memset(&f, 0, sizeof(f)); switch(amlenv->ps[0]){ case 0: /* reserved field */ amlnext(); off += ppkglen(); diprint("%#!off = %#x\n", off/8); break; case 1: /* access field */ amlnext(); amlnext(); amlnext(); break; default: amlclose(amlenv->res); amlenv->res = nil; n = pnamechar(); if(n == nil) amlerror("bad name"); amlenv->res = nil; f.off = off; kstrdup(&f.fname, n->sval); amlclose(n); f.len = ppkglen(); fo = amlnew(Ofield); fo->field = f; *ol = fo; ol = &fo->next; off += f.len; diprint("%#!field '%s' %d\n", f.fname, f.len); } } popamlerror(); return amlenv->res = lo; } static int isintop(Op *op) { return op->x == opbyteprefix || op->x == opwordprefix || op->x == opdwordprefix || op->x == opqwordprefix; } static int isstrop(Op *op) { return op->x == opstrprefix || op->x == opnamechar; } /* * Decode arguments according to fmt. * See aml.h:/Pargs types in aml.h. */ static uchar* pargs(void) { uchar *newpe, *oldpe; Aobj *o, **args; char *fmt; Env env2; int len, flags, hasbody, hasargs; fmt = amlenv->op->args; if(fmt[0] == 0){ /* no args; optimize */ if(!isstrop(amlenv->op)) diprint("\n"); return amlenv->pe; } args = amlenv->args; hasbody = amlenv->op->flags&Bdy; oldpe = nil; /* * 'l' limits the length of the program for the following * items. We must enforce the limit and restore env later. */ newpe = amlenv->pe; if(fmt[0] == 'l'){ newpe = amlenv->ps; len = ppkglen(); newpe += len; if(newpe > amlenv->pe){ amlwarn("pargs: overlap (truncating)\n"); newpe = amlenv->pe; }else oldpe = amlenv->pe; fmt++; diprint("[%#x]", len); } hasargs = fmt[0] != 0; if(hasargs) diprint("(\n"); else diprint("\n"); amlenv->pe = newpe; flags = amlenv->flags; if(wasamlerror()){ amlenv->flags = flags; if(oldpe != nil) amlenv->pe = oldpe; amlerror(nil); } for(; fmt[0] != 0 && amlenv->ps < amlenv->pe; fmt++){ amlclose(amlenv->res); amlenv->res = nil; switch(fmt[0]){ case 'b': o = pbyte(); break; case 'w': o = pword(); break; case 'd': o = pdword(); break; case 'f': o = pfieldlist(); break; case 'n': amlenv->flags |= Enocall; o = pnamechar(); amlenv->flags = flags; if(fmt[0] == 's' && (o == nil || o->type != Oname)) amlerror("bad argument for '%c'", fmt[0]); break; case 't': amlenv->flags |= Enocall; if(fmt[1] != 0 && fmt[1] != 't') amlerror("pargs bug: 't' is not last"); if(amlenv->ps[0] == OpZero){ o = amlenv->res = nil; /* null target */ amlnext(); /* not an int */ break; } /* else fall */ case 'B': case 'D': case 'E': case 'F': case 'I': case 'M': case 'O': case 'o': case 'P': case 'R': case 'S': case 's': /* * pamlobj may evaluate ops with args * we must save the current state * and leave the resulting object at amlenv->res * where it belongs. */ amlpush(&env2, "pargs", nil, newpe); if(wasamlerror()){ amlpop(); amlerror(nil); } o = pamlobj(); popamlerror(); amlenv->res = nil; /* env2's res */ amlpop(); amlenv->res = o; /* env's res */ amlenv->flags = flags; break; case 'l': amlerror("pargs bug: 'l' is not first"); return nil; default: amlerror("pargs bug: bad arg fmt %c", fmt[0]); return nil; } diprint("%#!arg%d/%c %O\n", amlenv->nargs, fmt[0], o); if((amlenv->flags&Enoeval) == 0 && o == nil && fmt[0] != 't') amlerror("null argument for '%c'", fmt[0]); if(amlenv->nargs == nelem(amlenv->args)) amlerror("pargs: increase Nargs"); if((amlenv->flags&Enoeval) == 0 && fmt[0] >= 'A' && fmt[0] <= 'Z') args[amlenv->nargs++] = amlcast(o, fmt[0]); else{ args[amlenv->nargs++] = o; if(o != nil) incref(o); } amlclose(o); amlenv->res = nil; } if(fmt[0] != 0) amlerror("argument expected"); if(oldpe != nil) amlenv->pe = oldpe; popamlerror(); if(hasargs || hasbody){ diprint("%!"); if(hasargs) diprint(")"); if(hasbody) diprint("{"); diprint("\n"); } return newpe; } /* * Pure paranoia. Double check that we got args ok. * They should be, but bugs in arg. processing may show up * and we might make a mistake in the op tables below. */ static Aobj** checkargs(char *s) { Aobj **args; if(amlenv->flags&Enoeval) return amlenv->args; args = amlenv->args; for(; s != nil && s[0] != 0; s++){ if(*args == nil) if(s[0] == 't') break; else amlerror("checkargs: argument expected"); switch(s[0]){ case 'B': amltcheck(*args, Obuf, 1); break; case 'b': case 'w': case 'd': case 'q': case 'I': amltcheck(*args, Oint, 1); break; case 'D': amltcheck(*args, Odecstr, 0); break; case 'E': amltcheck(*args, Oevent, 1); break; case 'F': amltcheck(*args, Ofield, 1); break; case 'M': amltcheck(*args, Omutex, 1); break; case 'n': case 'N': amltcheck(*args, Oname, 0); break; case 'O': case 'o': break; case 'l': case 'L': case 'f': case 'P': amltcheck(*args, Opkg, 1); break; case 'R': amltcheck(*args, Oreg, 1); break; case 's': case 'S': amltcheck(*args, Ostr, 0); break; case 't': if((*args)->type != Oname && (*args)->type != Olocal && (*args)->type != Oarg) amlerror("checkargs: %O is not a target", *args); break; default: panic("aml: checkargs: unknown argument letter %c", s[0]); } args++; } return amlenv->args; } static void fetch(void) { Env *env; int ic; uchar *ps; env = amlenv; ps = env->ps; env->op = decode(env->ps, env->pe, &ic); env->ic = ic; if(env->op == nil) amlerror("unknown instr %#04x at %#p", env->ic, ps); if(env->ps[0] == OpExtprefix && env->ps < env->pe) amlnext(); if(!isintop(env->op) && !isstrop(env->op)) diprint("%!%s", env->op->name); env->op->ncalls++; } /* * Process aml for a single object and return it. */ static Aobj* pamlobj(void) { Aobj *o; Op *op; int i; int ic; amlenv->opp0 = amlenv->ps; fetch(); amlclose(amlenv->res); amlenv->res = nil; for(i = 0; i < amlenv->nargs; i++){ amlclose(amlenv->args[i]); amlenv->args[i] = nil; } amlenv->nargs = 0; op = amlenv->op; ic = amlenv->ic; if(op->args != nil){ amlnext(); amlenv->argpe = pargs(); }else{ amlenv->argpe = amlenv->pe; if(!isintop(op) && !isstrop(op)) diprint("\n"); } amlenv->ic = ic; /* pargs may have changed it */ if((amlenv->flags&Enoeval) == 0 || (op->flags&Dry) != 0) op->x(); o = amlenv->res; if((op->flags&Bdy) != 0) diprint("%!}\n"); if(isintop(op) && o != nil) diprint("%!%s %#ullx\n", amlenv->op->name, o->ival); if(amlenv->flags&Eedump) amlenvdump(amlenv); if(amlenv->flags&Escheck) amlstackcheck(); return o; } /* * Process all objects in [ps:pe] and return a list with them. * or throw them away after evaluating if keep == 0. */ Aobj* pamlblock(int keep) { Aobj **ol, *o; int i; assert(amlenv->olist.hd == nil); ol = &amlenv->olist.hd; amlenv->olist.n = 0; for(i = 0; amlenv->ps < amlenv->pe && i < 1000; i++){ pamlobj(); o = amlenv->res; if(o != nil) if(keep){ *ol = o; if(o->next != nil) panic("pamlblock: non-null next: %O", *ol); ol = &o->next; amlenv->olist.n++; }else amlclose(o); amlenv->res = nil; } if(i == 1000) panic("pamlblock: loop or huge pamlblock?"); return amlenv->olist.hd; } /* * Plain (1-byte) operations. * The argument string indicates which arguments are expected * for each op. * A nil as argument str. means that no arg processing is done * (env->ps points to the operation, which must call amlnext() * to advance the pc). * An empty ("") argument str. means that there is no argument, * but argument processing will happen and advance the PC * to point past the last argument (i.e., past the op code). * A non-empty argument str. means that pargs will attempt to * build objects for arguments as explained in aml.h:/Pargs. * See also the comment at the start of amlrt.c. */ static Op amlops[256] = { [OpZero] {opnum, "zero", Dry, ""}, [OpOne] {opnum, "one", Dry, ""}, [OpAlias] {opalias, "alias", 0, "On"}, [OpName] {opname, "name", 0, "nO"}, [OpByteprefix] {opbyteprefix, "byteprefix", Dry, nil}, [OpWordprefix] {opwordprefix, "wordprefix", Dry, nil}, [OpDwordprefix] {opdwordprefix, "dwordprefix", Dry, nil}, [OpStrprefix] {opstrprefix, "strprefix", Dry, ""}, [OpQwordprefix] {opqwordprefix, "qwordprefix", Dry, nil}, [OpScope] {opscope, "scope", Bdy|Dry, "ln"}, [OpBuffer] {opbuf, "buf", Dry, "lI"}, [OpPackage] {oppackage, "package", Bdy|Dry, "lb"}, [OpVarpackage] {oppackage, "varpackage", Bdy|Dry, "lI"}, [OpMethod] {opscope, "method", Dry, "lnb"}, [OpDnameprefix] {opnamechar, "dnameprefix", Dry, nil}, [OpMnameprefix] {opnamechar, "mnameprefix", Dry, nil}, [OpExtprefix] {nil, "extprefix", Dry, nil}, [OpRootchar] {opnamechar, "rootchar", Dry, nil}, [OpParentprefix] {opnamechar, "parentprefix", Dry, nil}, [OpNamechar] {opnamechar, "namechar", Dry, nil}, [OpLocal0] {oplocal, "local0", Dry, nil}, [OpLocal1] {oplocal, "local1", Dry, nil}, [OpLocal2] {oplocal, "local2", Dry, nil}, [OpLocal3] {oplocal, "local3", Dry, nil}, [OpLocal4] {oplocal, "local4", Dry, nil}, [OpLocal5] {oplocal, "local5", Dry, nil}, [OpLocal6] {oplocal, "local6", Dry, nil}, [OpLocal7] {oplocal, "local7", Dry, nil}, [OpArg0] {oparg, "arg0", Dry, nil}, [OpArg1] {oparg, "arg1", Dry, nil}, [OpArg2] {oparg, "arg2", Dry, nil}, [OpArg3] {oparg, "arg3", Dry, nil}, [OpArg4] {oparg, "arg4", Dry, nil}, [OpArg5] {oparg, "arg5", Dry, nil}, [OpArg6] {oparg, "arg6", Dry, nil}, [OpStore] {opcast, "store", 0, "Ot"}, [OpRefof] {oprefof, "refof", 0, "o"}, [OpAdd] {opbinaryt, "add", 0, "IIt"}, [OpConcat] {opconcat, "concat", 0, "OOt"}, [OpSub] {opbinaryt, "sub", 0, "IIt"}, [OpInc] {opincdec, "inc", 0, "I"}, [OpDec] {opincdec, "dec", 0, "I"}, [OpMultiply] {opbinaryt, "multiply", 0, "IIt"}, [OpDivide] {opdivide, "divide", 0, "IItt"}, [OpShl] {opbinaryt, "shl", 0, "IIt"}, [OpShr] {opbinaryt, "shr", 0, "IIt"}, [OpAnd] {opbinaryt, "and", 0, "IIt"}, [OpNand] {opbinaryt, "nand", 0, "IIt"}, [OpOr] {opbinaryt, "or", 0, "IIt"}, [OpNor] {opbinaryt, "nor", 0, "IIt"}, [OpXor] {opbinaryt, "xor", 0, "IIt"}, [OpNot] {opunaryt, "not", 0, "It"}, [OpLeftsetbit] {opunaryt, "leftsetbit", 0, "It"}, [OpRightsetbit] {opunaryt, "rightsetbit", 0, "It"}, [OpDerefof] {opderefof, "derefof", 0, "o"}, [OpConcatres] {opconcatres, "concatres", 0, "BBt"}, [OpMod] {opbinaryt, "mod", 0, "IIt"}, [OpNotify] {opnotify, "notify", 0, "OI"}, [OpSizeof] {opunaryobj, "sizeof", 0, "O"}, [OpIndex] {opindex, "index", 0, "OIt"}, [OpMatch] {opmatch, "match", 0, "PbIbII"}, [OpMkdwordfield] {opmkxfield, "mkdwordfield", 0, "BIn"}, [OpMkwordfield] {opmkxfield, "mkwordfield", 0, "BIn"}, [OpMkbytefield] {opmkxfield, "mkbytefield", 0, "BIn"}, [OpMkbitfield] {opmkxfield, "mkbitfield", 0, "BIn"}, [OpObjecttype] {opunaryobj, "objecttype", 0, "O"}, [OpMkqwordfield] {opmkxfield, "mkqwordfield", 0, "BIn"}, [OpLand] {oplandor, "land", 0, "II"}, [OpLor] {oplandor, "lor", 0, "II"}, [OpLnot] {oplnot, "lnot", 0, "I"}, [OpLequal] {oplcmp, "lequal", 0, "OO"}, [OpLgreater] {oplcmp, "lgreater", 0, "OO"}, [OpLless] {oplcmp, "lless", 0, "OO"}, [OpTobuf] {opcast, "tobuf", 0, "Bt"}, [OpTodecstr] {opcast, "todecstr", 0, "Dt"}, [OpTohexstr] {opcast, "tohexstr", 0, "St"}, [OpToint] {opcast, "toint", 0, "It"}, [OpTostr] {optostr, "tostr", 0, "BIt"}, [OpCopyobject] {opcast, "copyobject", 0, "On"}, [OpMid] {opmid, "mid", 0, "OIIt"}, [OpContinue] {opjump, "continue", 0, ""}, [OpIf] {opif, "if", Bdy|Dry, "lI"}, [OpElse] {opelse, "else", Bdy|Dry, "l"}, [OpWhile] {opif, "while", Bdy|Dry,"lI"}, [OpNop] {opnop, "nop", 0, ""}, [OpReturn] {opreturn, "return", 0, "O"}, [OpBreak] {opjump, "break", 0, ""}, [OpBreakpoint] {opnop, "breakpoint", 0, ""}, [OpOnes] {opnum, "ones", Dry, ""}, }; /* * extended operation, found after OpExtprefix. * codes may be found also in the plain (non-extended) operation * table. But env->op will always point to the correct operation. * When env->ic is looked at to switch among several codes, * context makes it clear which one is the operation at hand. */ static Xop amlxops[] = { {OpDevice, opscope, "device", Dry, "ln"}, {OpDataregion, opdataregion, "dataregion", 0, "nSSS"}, {OpField, opfield, "field", 0, "lRbf"}, {OpBankfield, opbankfield, "bankfield", 0, "lRnIbf"}, {OpIndexfield, opindexfield, "indexfield", 0, "lFFbf"}, {OpCondrefof, opcondrefof, "condrefof", 0, "nt"}, {OpMutex, opmutex, "mutex", 0, "nb"}, {OpEvent, opevent, "event", 0, "n"}, {OpMkfield, opmkxfield, "mkfield", 0, "BIIn"}, {OpOpregion, opopregion, "opregion", 0, "nbII"}, {OpLoadtable, oploadtable, "loadtable", 0, "SSSSSB"}, {OpLoad, opload, "load", 0, "Ot"}, {OpStall, opsleep, "stall", 0, "I"}, {OpSleep, opsleep, "sleep", 0, "I"}, {OpAcquire, opacquire, "acquire", 0, "Mw"}, {OpSignal, opsignal, "signal", 0, "E"}, {OpWait, opwait, "wait", 0, "EI"}, {OpReset, opreset, "reset", 0, "E"}, {OpRelease, oprelease, "release", 0, "M"}, {OpFrombcd, opunaryt, "frombcd", 0, "It"}, {OpTobcd, opunaryt, "tobcd", 0, "It"}, {OpUnload, opunload, "unload", 0, "I"}, {OpRevision, opnum, "revision", 0, ""}, {OpDebug, opnum, "debug", 0, ""}, {OpFatal, opfatal, "fatal", 0, "bdI"}, {OpTimer, opnum, "timer", 0, ""}, {OpProcessor, opscope, "processor", Dry, "lnbdb"}, {OpPwrrsrc, opscope, "pwrrsrc", Dry, "lnbw"}, {OpThermalzone, opscope, "thermalzone", Dry, "ln"}, {0, nil, nil, nil}, }; void initamlops(void) { int i; for(i = 'A'; i <= 'Z'; i++){ assert(amlops[i].name == nil); amlops[i].name = "namechar"; amlops[i].x = opnamechar; amlops[i].flags = Dry; } } static Op* decode(uchar* ps, uchar *pe, int *icp) { Op *op; Xop *xop; int ic; assert(ps < pe); *icp = ic = ps[0]; if(ic == OpExtprefix){ if(ps >= pe-1) return nil; *icp = ic = ps[1]; for(xop = amlxops; xop->ic != 0 && xop->ic != ic; xop++) ; if(xop->ic == 0) return nil; op = &xop->op; }else op = &amlops[ic]; if(op == nil || op->x == nil) return nil; else return op; } void dumpopstats(void) { Xop *xop; int i; dprint("\ncalls:\n"); for(i = 0; i < 256; i++) if(amlops[i].x != nil && amlops[i].ncalls != 0) dprint("%3d %s\n", amlops[i].ncalls, amlops[i].name); for(xop = amlxops; xop->ic != 0; xop++) if(xop->op.ncalls != 0) dprint("%3d %s\n",xop->op.ncalls,xop->op.name); } v->args; args = amlenv->args; for(; s != nil && s[0] != 0; s++){ if(*args == nil) if(s[0] == 't') break; else amlerror("checkargs: argument expected"); switch(s[0]){ case 'B': amltcheck(*args, Obuf, 1); break; case 'b': case 'w': case 'd': case 'q': case 'I': amltcheck(*args, Oint, 1); b9am/amlrt.c 664 0 0 57710 11333074432 10667ustar00nemosys/* * Advanced Configuration and Power Interface Specification 4.0. * Abstract Machine Language (AML) interpreter. * * This file provides run time support and configuration routines, * and contains the main entry point for the interpreter. * * See the aml.ms document before reading the implementation. */ #include "kernel.h" #include "acpi.h" #include "aml.h" Aname *acpins; /* namespace */ Env *amlenv; /* last (most nested) AML env */ Error amlerrstk; /* our error stack */ Aobj* acpilock; int amlflags; /* global debug flags when not evaluating */ static int naobj; /* number of allocated objects */ static int maxnaobj; /* max. number of allocated objects */ static int nanames; /* number of names allocated */ static int maxdepth; static uchar* minsp; static Aobj *freeobj; char* amltname(int t) { static char* amltnames[] = { "none", "BUG", "slice", "scope", "method", "dev", "proc", "pwr", "thermal", "ref", "local", "arg", "handle", }; if(t >= 0 && t < nelem(amltnames)) return amltnames[t]; switch(t){ case Obuf: return "buf"; case Oevent: return "event"; case Oint: return "int"; case Oname: return "name"; case Omutex: return "mutex"; case Oany: return "any"; case Opkg: return "pkg"; case Oreg: return "reg"; case Ostr: return "str"; case Odecstr: return "decstr"; case Ofield: return "field"; default: return "unknown"; } } void* amlmalloc(int l) { void *p; p = malloc(l); if(p == nil) panic("aml: no memory"); memset(p, 0, l); return p; } /* * Push a nested environment continuing evaluation * from where the parent is right now. * dot, ps, pe, and table may be nil when not a top-level environment, * and those of the current environment are used in that case. */ void amlpush(Env *new, char* name, uchar *ps, uchar *pe) { Env *env; memset(new, 0, sizeof(Env)); env = new->prev = amlenv; new->name = name; assert(env != nil || ps != nil); assert(env != nil || pe != nil); if(ps != nil && env != nil && (ps < env->ps || ps > env->pe)) amlwarn("amlpush: wrong ps at %#ulx", getcallerpc(&new)); if(pe != nil && env != nil && (pe < env->ps || pe > env->pe)) amlwarn("amlpush: wrong pe at %#ulx", getcallerpc(&new)); if(ps != nil) new->ps = ps; else new->ps = env->ps; if(pe != nil) new->pe = pe; else new->pe = env->pe; if(env != nil){ new->ic = env->ic; new->lvl = env->lvl+1; new->op = env->op; new->flags = env->flags; new->table = env->table; new->dot = env->dot; new->mp = env->mp; }else new->dot = acpins; if(new->lvl > maxdepth) maxdepth = new->lvl; if(minsp == 0 || (uchar*)&env < minsp) minsp = (uchar*)&env; amlenv = new; } /* * Pop out the current environment but update the parent's * idea of what's evaluated so far and the pc. This works fine * most of the time, but for calling methods. Upon return, the * calling sequence is responsible for restoring ps to point * past the call, and not past the method aml. */ Env* amlpop(void) { Env *old; Aobj *ol; int i; Mframe *mp; if(amlenv == nil) panic("pop on an empty env stack"); old = amlenv->prev; if(old != nil) old->ps = amlenv->ps; while(amlenv->olist.hd != nil){ ol = amlenv->olist.hd; amlenv->olist.hd = ol->next; amlclose(ol); } amlenv->olist.n = 0; for(i = 0; i < amlenv->nargs; i++){ amlclose(amlenv->args[i]); amlenv->args[i] = nil; } amlenv->nargs = 0; mp = amlenv->mp; if(mp != nil && (amlenv->prev == nil || amlenv->prev->mp != mp)){ /* method call; collect objects */ for(i = 0; i < mp->nargs; i++){ amlclose(mp->args[i]); mp->args[i] = nil; } for(i = 0; i < mp->nlcls; i++){ amlclose(mp->lcls[i]); mp->lcls[i] = nil; } } amlclose(amlenv->res); amlenv->res = nil; return amlenv = old; } int amlnext(void) { if(amlenv->ps >= amlenv->pe) amlerror("end of program"); amlenv->ps++; if(amlenv->ps < amlenv->pe) return amlenv->ps[0]; else return 0; } void popamlerror(void) { if (amlerrstk.nerr-- == 0) panic("popamlerror w/o wasamlerror"); } void amlerror(char* msg, ...) { va_list arg; char *s; char *se; if(msg != nil){ va_start(arg, msg); s = amlerrstk.msg; se = amlerrstk.msg + sizeof(amlerrstk.msg); s = vseprint(s, se, msg, arg); if(amlenv != nil) seprint(s, se, " at %08p", amlenv->ps); va_end(arg); if(!isamlbranch()) dprint("amlerror: %s\n", amlerrstk.msg); if(!isamlbranch() && amlenv != nil && (amlenv->flags&Eabort) != 0){ amlstackdump(); panic("acpi abort"); } } if(amlerrstk.nerr-- == 0) panic("aml error stack is empty"); gotolabel(&amlerrstk.label[amlerrstk.nerr]); } void amlwarn(char* msg, ...) { va_list arg; char buf[128]; char *s; s = buf; if(amlenv != nil) s = seprint(s, buf+sizeof(buf), "pc=%p: ", amlenv->ps); s = seprint(s, buf+sizeof(buf), "warning: "); va_start(arg, msg); vseprint(s, buf+sizeof(buf), msg, arg); va_end(arg); xprint("%s", buf); } /* * Is the error raised just a branch, and not an error? * we use "break", "continue", and "return" to implement those. */ int isamlbranch(void) { char *err; err = amlerrstk.msg; return strncmp(err, "break at", 8) == 0 || strncmp(err, "return at", 9) == 0 || strncmp(err, "continue at", 11) == 0; } Aobj* amlnew(int t) { Aobj *o; o = freeobj; if(o != nil){ freeobj = freeobj->next; if(o->type != Ofree) panic("acpi: free object has been used"); memset(o, 0, sizeof(Aobj)); }else o = amlmalloc(sizeof(Aobj)); o->type = t; o->ref = 1; naobj++; if(maxnaobj < naobj) maxnaobj = naobj; return o; } Aobj* amlnewi(int v) { Aobj *o; o = amlnew(Oint); o->ival = v; return o; } Aobj* amlnews(char* v) { Aobj *o; o = amlnew(Ostr); if(v == nil) v = ""; kstrdup(&o->sval, v); return o; } void amltcheck(Aobj *o, int t, int nullrefok) { uintptr pc; pc = getcallerpc(&o); if(o == nil) amlerror("amltcheck: %#p null object", pc); if(o->type != t) amlerror("amltcheck: %#p %O is not of type %#02x\n", pc, o, t); if(nullrefok != 0) return; switch(o->type){ case Oint: case Oscope: case Omethod: case Odev: case Oproc: case Opwr: case Othermal: case Ohandle: case Oevent: case Omutex: case Opkg: case Oreg: return; case Ofield: if(o->field.idx != nil) if(o->field.idx->type != Ofield || o->field.idxval == nil) amlerror("bad index in field"); if(o->field.bank != nil){ if(o->field.bank->type != Ofield || o->field.bankval == nil) amlerror("bad bank in field"); if(o->field.data == nil || o->field.data->type != Ofield) amlerror("bad data in field"); } if(o->field.idx != nil || o->field.reg != nil) return; break; case Obuf: if(o->buf.p != nil) return; break; case Oname: case Ostr: case Odecstr: if(o->sval != nil) return; break; case Oslice: if(o->slice.src != nil){ if(o->slice.src->type != Obuf) amlerror("slice src is not a buffer"); return; } break; case Oref: if(o->oref.o != nil) return; break; default: panic("amltcheck: %#p objtype is %s", pc, amltname(o->type)); } amlerror("amltcheck: %#p %O has a null value", pc, o); } static void freeres(Res *res) { Res *r; while(res != nil){ r = res; res = r->next; if(r->type == Rpir) amlclose(r->pir.src); free(r); } } void amlclean(Aobj *o) { Aobj *l; switch(o->type){ case Onone: case Oint: case Oscope: case Omethod: case Opwr: case Oproc: case Othermal: case Ohandle: case Omutex: case Oevent: break; case Odev: free(o->dev.name); freeres(o->dev.res); freeres(o->dev.other); o->dev.name = nil; o->dev.res = o->dev.other = nil; break; case Opkg: while(o->list.hd != nil){ l = o->list.hd; o->list.hd = l->next; amlclose(l); } break; case Oreg: if(o->reg.p != nil) vunmap(o->reg.p, o->reg.len); break; case Oref: case Oarg: case Olocal: amlclose(o->oref.o); break; case Ostr: case Odecstr: case Oname: free(o->sval); break; case Ofield: amlclose(o->field.reg); amlclose(o->field.bank); amlclose(o->field.bankval); free(o->field.fname); amlclose(o->field.idx); amlclose(o->field.idxval); amlclose(o->field.data); break; case Oslice: amlclose(o->slice.src); break; case Obuf: free(o->buf.p); break; default: panic("amlclose: unknown type %#x\n", o->type); } o->type = Onone; } void amlclose(Aobj *o) { if(o == nil || decref(o) > 0) return; if(o->type == Ofree) panic("acpi: double free"); if(o->name != nil) panic("amlclose: bound %O", o); naobj--; amlclean(o); o->type = Ofree; o->next = freeobj; freeobj = o; } static Aname* newname(Aname *p, char *n) { Aname *nm; nm = amlmalloc(sizeof(Aname)); strncpy(nm->s, n, 4); nm->s[4] = 0; nm->parent = p; nm->path = nil; nm->hasdevs = 0; if(p != nil){ nm->path = amlmalloc(strlen(p->path) + 1 + 4 + 1); strcpy(nm->path, p->path); if(p->parent != nil) strcat(nm->path, "/"); strcat(nm->path, nm->s); if(p->childtl != nil) p->childtl->sibling = nm; p->childtl = nm; if(p->childhd == nil) p->childhd = nm; }else kstrdup(&nm->path, n); if(amlenv != nil && amlenv->mp != nil){ /* record table and method call, for unbind */ nm->table = amlenv->table; nm->mnext = amlenv->mp->binds; amlenv->mp->binds = nm; } nanames++; return nm; } /* Didn't they have enough? */ static int amlmatch(char *n, char *s) { while(n[0] != 0 && n[0] == s[0]){ n++; s++; } while(n[0] == '_' && s[0] == 0) n++; return n[0] == 0 && s[0] == 0; } static Aname* walkup(Aname *n, char *s) { Aname *cn; for(; n != nil; n = n->parent) for(cn = n->childhd; cn != nil; cn = cn->sibling){ if(amlmatch(cn->s, s)) return cn; } return nil; } Aname* amlwalk(char *s, int how, Aname *dot) { Aname *n, *cn; char *d; char *els[20]; int nels, i; n = dot; if(n == nil) if(amlenv == nil) n = acpins; else n = amlenv->dot; if(0 && how == Walkns) dbprint("walk '%s' dot=%s\n", s, n->path); if(strlen(s) == 4 && how == Walkns) /* NS search rule */ return walkup(n, s); while(s[0] == '\\' || s[0] == '/'){ while(n->parent != nil) n = n->parent; s++; } while(s[0] == '^'){ if(n->parent != nil) n = n->parent; s++; } if(s[0] == 0) return n; d = nil; kstrdup(&d, s); nels = getfields(d, els, nelem(els), 0, "./"); for(i = 0; n != nil && i < nels; i++){ for(cn = n->childhd; cn != nil; cn = cn->sibling) if(amlmatch(cn->s, els[i])) break; if(cn == nil) if(how == Walkcreating) cn = newname(n, els[i]); else{ free(d); return nil; } n = cn; } if(n == nil && how == Walkcreating) panic("bug: amlwalk: nil name but we are creating it"); free(d); return n; } /* * objects may be bound to more than one name. * we only record the first one in the object. */ void amlbind(char *s, Aobj *o) { Aname *n; n = amlwalk(s, Walkcreating, nil); if(n == nil) amlerror("can't bind '%s'", s); incref(o); if(n->o != nil){ n->o->name = nil; amlclose(n->o); } n->o = o; if(o->name == nil) o->name = n; dbprint("bind %s -> %O\n", n->path, o); if(o->type == Odev) for(n = n->parent; n != nil; n = n->parent) n->hasdevs++; } void amlunbind(Aname *n) { Aname *pn, *l, *prev; dbprint("unbind %s\n", n->path); pn = n->parent; if(pn == nil) panic("acpi: unbind of root name"); prev = nil; for(l = pn->childhd; l != nil && l != n; l = l->sibling) prev = l; if(l == nil) panic("acpi: name not in parent"); if(prev == nil) pn->childhd = n->sibling; else prev->sibling = n->sibling; if(n == pn->childtl) pn->childtl = prev; n->o->name = nil; amlclose(n->o); while(n->childhd != nil) amlunbind(n->childhd); free(n->path); free(n); } static int tfmt(Fmt *f) { char buf[80]; int n; n = va_arg(f->args, int); assert(n >= 0); switch(n){ case 0: return fmtstrcpy(f, ""); case 1: return fmtstrcpy(f, "\t"); default: if(n >= sizeof(buf)/3-3) n = sizeof(buf)/3-3; buf[0] = '\t'; n--; memset(buf+1, ' ', n*3); buf[1+n*3] = 0; return fmtstrcpy(f, buf); } } void dumpstats(void) { dprint("\n%d objects allocated\n", naobj); dprint("\n%d objects allocated at most \n", maxnaobj); dprint("%d names allocated\n", nanames); dprint("max stack depth %d\n", maxdepth); if(minsp != nil) dprint("max stack size %ld\n", (uchar*)acpistackbottom - minsp); } void amlenvdump(Env *e) { int i, lvl; Aobj *o; Mframe *mp; lvl = 0; dprint("%>env %s %#p prev %#p ps %#p pe %#p\n", lvl, e->name, e, e->prev, e->ps, e->pe); dprint("%>flg %#x lvl %d ic %#x opp0 %#p", lvl, e->flags, e->lvl, e->ic, e->opp0); if(e->flags&Enoeval) dprint(" noeval"); if(e->op != nil) dprint(" op=%s", e->op->name); if(e->dot != nil) dprint(" dot=%s\n", e->dot->path); else dprint("\n"); lvl++; if(e->res != nil) dprint("%>res %O\n", lvl, e->res); for(i = 0; i < nelem(e->args); i++) if(e->args[i] != nil) if(i < e->nargs) dprint("%>arg%d %O\n", lvl, i, e->args[i]); else panic("amlenvdump: argument used beyond nargs"); mp = e->mp; if(mp != nil && (e->prev == nil || e->prev->mp != mp)){ dprint("%>method frame:\n", lvl); for(i = 0; i < mp->nargs; i++) if(mp->args[i] != nil) dprint("%>marg%d %O\n", lvl+1, i, mp->args[i]); for(i = 0; i < mp->nlcls; i++) if(mp->lcls[i] != nil) dprint("%>lcl%d %O\n", lvl, i, mp->lcls[i]); } for(o = e->olist.hd; o != nil; o = o->next) dprint("%>obj %O\n", lvl, o); dprint("\n"); } void amlstackdump(void) { Env *e; xprint("aml stack:\n"); for(e = amlenv; e != nil; e = e->prev) amlenvdump(e); } void dumpns(Aname *ns, int d, int objs) { Aname *cn; xprint("%>%s", d, ns->path); if(objs && ns->o != nil && ns->o->type != Oscope) xprint("= %O", ns->o); if(ns->childhd != nil){ xprint(" {\n"); for(cn = ns->childhd; cn != nil; cn = cn->sibling) dumpns(cn, d+1, objs); xprint("%>}\n", d); }else xprint("\n"); } static int Pfmt(Fmt *f) { int lvl; if(amlenv == nil) return 0; lvl = amlenv->lvl; if(f->flags & FmtSharp) lvl++; return fmtprint(f, "%08p: %>", amlenv->ps, lvl); } static int Ofmt(Fmt *f) { static int verb = 0; Aobj *o, *ol; char str[40], *s; int i; o = va_arg(f->args, Aobj*); if(o == nil) return fmtprint(f, ""); str[0] = 0; s = str; if(verb || (f->flags&FmtSharp) != 0 || (o->type != Ostr && o->type != Oint && o->type != Ostr)) s = seprint(str, str+sizeof(str), "%s", amltname(o->type)); if(verb || (f->flags&FmtSharp) != 0){ s = seprint(s, str+sizeof(str), " r=%uld %#p", o->ref, o); if(o->name != nil) s = seprint(s, str+sizeof(str), " bound"); } switch(o->type){ case Oint: case Ohandle: return fmtprint(f, "%s %#ullx", str, o->ival); case Oname: case Ostr: case Odecstr: return fmtprint(f, "%s \"%s\"", str, o->sval); case Ofield: fmtprint(f, "%s %s", str, o->field.fname); if(o->field.idx != nil) fmtprint(f, " idx %s[%#llx]%s %#ulx:%uld[%#x]", o->field.idx->field.fname, o->field.idxval->ival, o->field.data->field.fname, o->field.off/8, o->field.off&7, o->field.len); else { if(o->field.reg != nil) fmtprint(f, " %s", o->field.reg->name->s); if(o->field.bank != nil) fmtprint(f, " bnk %s[%#llx]", o->field.bank->field.fname, o->field.bankval->ival); fmtprint(f, " %#ulx:%uld[%#x]", o->field.off/8, o->field.off&7, o->field.len); } return fmtprint(f, " a%xs%xu%x", o->field.accsz, o->field.locking, o->field.update); case Opkg: fmtprint(f, "%s[%d] {", str, o->list.n); i = 0; for(ol = o->list.hd; ol != nil; ol = ol->next){ if(o->list.n > 5 || ol->type == Opkg) fmtprint(f, "\n\t\t\t[%2d] ", i++); fmtprint(f, "%O%s", ol, ol->next ? ", " : ""); } return fmtprint(f, "}"); case Obuf: fmtprint(f, "%s %#p[%#lx]{", str, o, o->buf.len); if(o->buf.len > 6) fmtprint(f, "\n\t\t\t"); for(i = 0; i < o->buf.len; i++){ fmtprint(f, "%02x ", o->buf.p[i]); if((i % 16) == 15 && o->buf.len > 6) fmtprint(f, "\n\t\t\t"); } return fmtprint(f, "}"); case Oslice: fmtprint(f, "%s %#ulx:%ld[%#ulx]", str, o->slice.off/8, o->slice.off&7, o->slice.len); if(o->slice.src != nil && o->slice.src->type == Obuf) return fmtprint(f, " src %#p", o->slice.src); else return fmtprint(f, " src %O", o->slice.src); case Oscope: return fmtprint(f, "%s %s", str, o->scope.name); case Omethod: if(verb || (f->flags&FmtSharp) != 0) return fmtprint(f, "%s %s/%d %s sl%d ps %#p pe %#p", str, o->method.name, o->method.nargs, o->method.isexcl ? "excl " : "", o->method.synclvl, o->method.ps, o->method.pe); else return fmtprint(f, "%s %s/%d %#p", str, o->method.name, o->method.nargs, o->method.ps); case Odev: fmtprint(f, "%s %s", str, o->dev.name); if(o->dev.pnpid != nil) fmtprint(f, " pnp %s", o->dev.pnpid); return fmtprint(f, " uid %#ullx addr %#llux", o->dev.uid, o->dev.addr); case Oproc: return fmtprint(f, "%s %s id %#x baddr %#ulx blen %d", str, o->proc.name, o->proc.id, o->proc.baddr, o->proc.blen); case Opwr: return fmtprint(f, "%s %s syslvl %#x rorder %#x", str, o->pwr.name, o->pwr.syslvl, o->pwr.rorder); case Othermal: return fmtprint(f, "%s %s", str, o->thermal.name); case Oreg: return fmtprint(f, "%s %s base %#ullx p %#p len %#ullx", str, acpiregstr(o->reg.spc), o->reg.base, o->reg.p, o->reg.len); case Omutex: case Oevent: return fmtprint(f, "%s %#p", str, o); case Oarg: case Olocal: seprint(s, str+sizeof(str), "#%d ", o->oref.no); /* fall */ case Oref: if(o->oref.o == nil) return fmtprint(f, "%s", str); if(verb || (f->flags&FmtSharp) != 0) return fmtprint(f, "%s-> %#O", str, o->oref.o); else return fmtprint(f, "%s-> %O", str, o->oref.o); default: return fmtprint(f, "", o->type); } } Aobj* amlcall(Aobj *mo, Aobj **args, Atable *table, int flags) { Env toplvl; Mframe mfr; Aobj *r; memset(&mfr, 0, sizeof(mfr)); amlpush(&toplvl, "toplvl", mo->method.ps, mo->method.pe); amlenv->table = table; amlenv->flags = flags; amlenv->mp = 𝔪 r = nil; if(!wasamlerror()){ r = opcall(mo, args); amlenv->res = nil; /* don't close r */ popamlerror(); if(r == nil) r = amlnewi(0); /* return something */ } amlpop(); return r; } /* * Evaluate code is [ps:pe]. We pretend we are a method call * so that dry runs may still find arguments and locals. */ char* amleval(uchar *ps, uchar *pe, Aobj **po, Aname *n, Atable* table, int flags) { Env toplvl; Aobj *o; Mframe mfr; memset(&mfr, 0, sizeof(mfr)); mfr.nargs = Nmargs; amlpush(&toplvl, "toplvl", ps, pe); if(n != nil) amlenv->dot = n; amlenv->table = table; amlenv->flags = flags; amlenv->mp = 𝔪 if(wasamlerror()){ amlpop(); return amlerrstk.msg; } o = pamlblock(Dontkeep); if(po != nil) *po = o; else amlclose(o); popamlerror(); amlpop(); return nil; } static void osi(Aobj*) { int i; Aobj *arg; static char *osis[] = { "Windows 2000", "Windows 2001", "Windows 2001.1", "Windows 2001 SP0", "Windows 2001 SP1", "Windows 2001 SP2", "Windows 2001 SP3", "Windows 2001 SP4", "Windows 2006", }; arg = amlenv->mp->args[0]; if(arg == nil || arg->type != Ostr) amlerror("argument is not a string"); amlenv->res = amlnewi(0); for(i = 0; i < nelem(osis); i++) if(strcmp(arg->sval, osis[i]) == 0){ amlenv->res->ival = 1; break; } amlerror("return"); } /* * Most of the _INI AML code I've seen defines the OS as "Linux" * if it identifies itself as BSD. However, BSD code identifies itself * as windows. * We could either impersonate Windows or Linux. Otherwise, all the * bugs will be for us (besides our own ones). */ void amlinit(void) { Aobj *o; fmtinstall('O', Ofmt); fmtinstall('>', tfmt); fmtinstall('!', Pfmt); initamlops(); acpins = newname(nil, "/"); o = amlnews("Microsoft Windows NT"); /* sic */ amlbind("_OS_", o); amlclose(o); o = amlnewi(9); amlbind("_REV", o); amlclose(o); acpilock = amlnew(Omutex); amlbind("_GL_", acpilock); amlclose(acpilock); o = amlnew(Omethod); o->method.name = "_OSI"; o->method.nargs = 1; o->method.f = osi; o->method.ps = o->method.pe = (uchar*)osi; amlbind("_OSI", o); amlclose(o); } /* debug prints and output. only for a single thread. * Trying to get things ok for both bio and the kernel print. * b -> bind; x -> exec; i -> instruction; d -> any debug; 0 -> print */ void amlprint(char d, char *fmt, ...) { va_list v; int flags; flags = acpiflags; if(amlenv != nil) flags |= amlenv->flags; switch(d){ case 'x': if((flags & Exdump) == 0) return; break; case 'i': if((flags & Eidump) == 0) return; break; case 'b': if((flags & Ebdump) == 0) return; break; case 'o': if((flags & Eodump) == 0) return; break; case 'd': if((flags & Edebugflags) == 0) return; break; } va_start(v, fmt); vfprint(2, fmt, v); va_end(v); } /* * This part is pure paranoia and could probably go. * It's a full check on all objects on the aml stack, * to ensure that objects seem to be ok and that references are * ok also. * It's easy to make a mistake regarding RCs while implementing * some ops and this is a safeguard. * This check is activated when any debug flag is active. */ static int dumprefs; static int amlforall(Aname *n, int (*f)(Aobj*,void*), void *a) { int r, c; Aname *cn; r = 0; if(n->o != nil){ c = f(n->o, a); if(c != 0 && dumprefs) dprint("\tns %s %#p\n", n->path, n->o); r += c; } for(cn = n->childhd; cn != nil; cn = cn->sibling) r += amlforall(cn, f, a); return r; } static int forallargs(Env *e, Aobj **args, char *s, int nargs, int(*f)(Aobj*,void*), void* a) { int i, r, n; if(args == nil) return 0; r = 0; for(i = 0; i < nargs; i++) if(args[i] != nil){ n = f(args[i], a); if(n != 0 && dumprefs) dprint("\tenv %#p %s%d %#p\n", e, s, i, args[i]); r += n; } return r; } static int forallobjs(int (*f)(Aobj*,void*), void *a) { Env *e; int r, i, n; Aobj *o; Mframe *mp; r = 0; for(e = amlenv; e!= nil; e = e->prev){ if(e->res != nil){ n = f(e->res, a); if(n != 0 && dumprefs) dprint("\tenv %#p res %#p\n", e, e->res); r += n; } for(o = e->olist.hd; o != nil; o = o->next){ n = f(o, a); if(n != 0 && dumprefs) dprint("\tenv %#p olist %#p\n", e, o); r += n; } for(i = 0; i < e->nargs; i++){ if(e->args[i] == nil) continue; n = f(e->args[i], a); if(n != 0 && dumprefs) dprint("\tenv %#p arg%d %#p\n", e, i, e->args[i]); r += n; } mp = e->mp; if(mp != nil && (e->prev == nil || e->prev->mp != mp)){ r += forallargs(e, mp->args, "marg", mp->nargs, f, a); r += forallargs(e, mp->targs, "tmarg", Nmargs, f, a); r += forallargs(e, mp->lcls, "lcl", mp->nlcls, f, a); } } r += amlforall(acpins, f, a); return r; } enum {Nrefs = 1023}; static int addref(Aobj **refs, Aobj *o) { int i; for(i = 0; i < Nrefs; i++) if(refs[i] == nil){ refs[i] = o; return 1; }else if(refs[i] == o) return 0; panic("countrefs: increase Nrefs %d\n", Nrefs); return 1; } static int countrefs(Aobj *o, void *a) { Aobj **refs = a; Aobj *arg; Aobj *l; int nr; arg = refs[0]; refs++; if(o == nil) panic("aml: contrefs on nil obj"); if(o == arg) return 1; switch(o->type){ case Oref: case Olocal: case Oarg: if(o->oref.o == arg) return addref(refs, o); else if(o->oref.o != nil) return countrefs(o->oref.o, refs-1); break; case Ofield: nr = 0; if(o->field.reg == arg) nr += addref(refs, o); if(o->field.bank == arg) nr += addref(refs, o); if(o->field.bankval == arg) nr += addref(refs, o); if(o->field.idx == arg) nr += addref(refs, o); if(o->field.idxval == arg) nr += addref(refs, o); if(o->field.data == arg) nr += addref(refs, o); return nr; case Oslice: if(o->slice.src == arg) return addref(refs, o); break; case Opkg: for(l = o->list.hd; l != nil; l = l->next) if(l == arg) return addref(refs, o); break; } return 0; } static int checkobj(Aobj *o, void *) { int nrefs; Aobj **refs; refs = amlmalloc(sizeof(Aobj*) * (Nrefs+1)); if((char*)o < end || (char*)o > (char*)&o){ amlwarn("bad object address %#p", o); amlstackdump(); panic("acpi abort"); } refs[0] = o; nrefs = forallobjs(countrefs, refs); if(nrefs != o->ref){ amlwarn("object %#O\n\t%d refs found on stack.\n", o, nrefs); dumprefs=1; forallobjs(countrefs, refs); amlstackdump(); panic("acpi stack check failed"); } free(refs); return 0; } void amlstackcheck(void) { forallobjs(checkobj, nil); } && o->type != Oint && o->type != Ostr)) s = seprint(s9am/apic.c 664 0 0 13470 11332325603 10455ustar00nemosys#include "kernel.h" #include "acpi.h" #include "aml.h" /* * Configure apics and interrupts. * This is only necessary for: * - hot plugging of processors. * - parsing entries obtained from other systems. * * MADT entries only guarantee that the first lapic is * for the boot processor. Processing can't be linear * because entries for an apic might be found before * the entry declaring the apic. * * System interrupt numbers are virtual, with each * ioapic knowing the base number for its first input. * * Interrupt flags are always converted into Apic flags. */ static Madt *apics; static void acpilapic(Apicst *a) { Apicst *l; uint lint[2]; lint[0] = lint[1] = -1; for(l = apics->st; l != nil; l = l->next) if(l->type == ASlnmi && l->lnmi.pid == a->lapic.pid){ if(l->lnmi.lint != 0 && l->lnmi.lint != 1) dprint("acpilapic: bad lint %d\n", l->lnmi.lint); else lint[l->lnmi.lint] = l->lnmi.flags; } acpictl("lapic %d %#llux %I %I", a->lapic.id, apics->laddr, lint[0], lint[1]); } void acpiapics(void) { Apicst *l; Aobj *o, *args[Nmargs]; Aname *n; int nlapics; if(apics == nil) return; n = amlwalk("/PIC", Walkdev, acpins); if(n != nil && n->o != nil && n->o->type == Omethod){ args[0] = amlnewi(Papic); args[1] = nil; o = amlcall(n->o, args, nil, acpiflags); if(o == nil) xprint("call to _PIC failed\n"); amlclose(o); } /* * Configure apics. The first one is always the boot processor. * Ensure IO apics do not get same ids than local apics. */ nlapics = 0; for(l = apics->st; l != nil; l = l->next) if(l->type == ASlapic){ nlapics++; acpilapic(l); } for(l = apics->st; l != nil; l = l->next) if(l->type == ASioapic){ acpictl("ioapic %d %#llux %d", l->ioapic.id + nlapics, l->ioapic.addr, l->ioapic.ibase); break; } /* * Add interrupt info now that we have configured all apics. */ for(l = apics->st; l != nil; l = l->next) switch(l->type){ case ASintovr: acpictl("int isa %d %d %I", l->intovr.irq, l->intovr.intr, l->intovr.flags); break; case ASnmi: acpictl("int isa 2 %d %I", l->nmi.intr, l->nmi.flags|IntNMI); break; } } static void dumpmadt(Madt *apics) { Apicst *st; if((acpiflags&Edebugflags) == 0) return; dprint("acpi: madt lapic addr %llux pcat %d:\n", apics->laddr, apics->pcat); for(st = apics->st; st != nil; st = st->next) switch(st->type){ case ASlapic: dprint("\tlapic pid %d id %d\n", st->lapic.pid, st->lapic.id); break; case ASioapic: case ASiosapic: dprint("\tioapic id %d addr %#llux ibase %d\n", st->ioapic.id, st->ioapic.addr, st->ioapic.ibase); break; case ASintovr: dprint("\tintovr irq %d intr %d %I\n", st->intovr.irq, st->intovr.intr, st->intovr.flags); break; case ASnmi: dprint("\tnmi intr %d %I\n", st->nmi.intr, st->nmi.flags); break; case ASlnmi: dprint("\tlnmi pid %d lint %d %I\n", st->lnmi.pid, st->lnmi.lint, st->lnmi.flags); break; case ASlsapic: dprint("\tlsapic pid %d id %d eid %d puid %d puids %s\n", st->lsapic.pid, st->lsapic.id, st->lsapic.eid, st->lsapic.puid, st->lsapic.puids); break; case ASintsrc: dprint("\tintr type %d pid %d peid %d" " iosv %d intr %d %I\n", st->type, st->intsrc.pid, st->intsrc.peid, st->intsrc.iosv, st->intsrc.intr, st->intsrc.flags); break; case ASlx2apic: dprint("\tlx2apic puid %d id %d\n", st->lx2apic.puid, st->lx2apic.id); break; case ASlx2nmi: dprint("\tlx2nmi puid %d intr %d %I\n", st->lx2nmi.puid, st->lx2nmi.intr, st->lx2nmi.flags); break; default: dprint("\t\n"); } dprint("\n"); } int acpimadt(uchar *p, int len) { uchar *pe; Apicst *st, *l, **stl; int stlen, id; apics = amlmalloc(sizeof(Madt)); apics->laddr = l32get(p+36); apics->pcat = l32get(p+40); apics->st = nil; stl = &apics->st; pe = p + len; for(p += 44; p < pe; p += stlen){ st = amlmalloc(sizeof(Apicst)); st->type = p[0]; st->next = nil; stlen = p[1]; switch(st->type){ case ASlapic: st->lapic.pid = p[2]; st->lapic.id = p[3]; if(l32get(p+4) == 0){ free(st); st = nil; } break; case ASioapic: st->ioapic.id = p[2]; st->ioapic.addr = l32get(p+4); st->ioapic.ibase = l32get(p+8); break; case ASintovr: st->intovr.irq = p[3]; st->intovr.intr = l32get(p+4); st->intovr.flags = l16get(p+8); break; case ASnmi: st->nmi.flags = IntNMI|l16get(p+2); st->nmi.intr = l32get(p+4); break; case ASlnmi: st->lnmi.pid = p[2]; st->lnmi.flags = IntNMI|l16get(p+3); st->lnmi.lint = p[5]; break; case ASladdr: apics->laddr = l64get(p+8); break; case ASiosapic: id = st->iosapic.id = p[2]; st->iosapic.ibase = l32get(p+4); st->iosapic.addr = l64get(p+8); for(l = apics->st; l != nil; l = l->next) if(l->type == ASioapic && l->ioapic.id == id){ l->ioapic = st->iosapic; free(st); st = nil; break; } break; case ASlsapic: st->lsapic.pid = p[2]; st->lsapic.id = p[3]; st->lsapic.eid = p[4]; st->lsapic.puid = l32get(p+12); if(l32get(p+8) == 0){ free(st); st = nil; }else kstrdup(&st->lsapic.puids, (char*)p+16); break; case ASintsrc: st->intsrc.flags = l16get(p+2); st->type = p[4]; st->intsrc.pid = p[5]; st->intsrc.peid = p[6]; st->intsrc.iosv = p[7]; st->intsrc.intr = l32get(p+8); st->intsrc.any = l32get(p+12); break; case ASlx2apic: st->lx2apic.id = l32get(p+4); st->lx2apic.puid = l32get(p+12); if(l32get(p+8) == 0){ free(st); st = nil; } break; case ASlx2nmi: st->lx2nmi.flags = IntNMI|l16get(p+2); st->lx2nmi.puid = l32get(p+4); st->lx2nmi.intr = p[8]; break; default: xprint("unknown APIC structure\n"); free(st); st = nil; } if(st != nil){ *stl = st; stl = &st->next; } } dumpmadt(apics); return 0; } t(v, fmt); vfprint(2, fmt, v); va_end(v); } /* * This part is pure paranoia and could probably go. * It's a full check on all objects on the aml stack, * to ensure that objects seem to be ok and9am/guide 664 0 0 467 11330051422 10350ustar00nemosyschanged files: files=(/sys/src/9/pc/devarch.c \ /sys/src/9/pc/main.c \ /sys/src/9/pc/mkfile \ /sys/src/9/pc/pcf \ /sys/src/9/port/chan.c \ /sys/src/9/port/initcode.c \ /sys/src/9/port/mkdevc \ /sys/src/9/port/portdat.h \ /sys/src/9/port/portfns.h \ ) for(f in $files) diff -n $f /n/sources/plan9/$f 9am/kernel.c 664 0 0 455 11323204522 10754ustar00nemosys/* * Compat. */ #include "kernel.h" #include "acpi.h" #include "aml.h" Biobuf* bout; void incref(Ref *r) { r->ref++; } int decref(Ref *r) { return --(r->ref); } void kstrdup(char **p, char *s) { char *prev; prev = *p; *p = strdup(s); if(*p == nil) sysfatal("no memory"); free(prev); } 9am/kernel.h 644 0 0 2355 11325612277 11014ustar00nemosys/* * Kernel compat. * This is to allow remaining code to be ready * for the kernel without manual editing. */ #include #include #include typedef struct Label { jmp_buf b; } Label; typedef struct Ref{ long ref; } Ref; typedef struct Pcidev{ int tbdf; }Pcidev; #define BusPCI 0 #define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8)) #define perfticks() nsec() #define setlabel(x) setjmp((x)->b) #define gotolabel(x) longjmp((x)->b, 1) #define panic(...) (Bterm(bout), print("\n"), print(__VA_ARGS__), print("\n"), abort()) #define delay(x) #define microdelay(x) #define vmap(a, b) ((void*)(a)) #define vunmap(a,b) USED(a) #define inb(x) x #define ins(x) x #define inl(x) x #define outb(x,y) USED(x);USED(y) #define outs(x,y) USED(x);USED(y) #define outl(x,y) USED(x);USED(y) #define pcicfgr8(a,b) b-b #define pcicfgw8(a,b,c) USED(a);USED(b);USED(c) #define pcicfgr16(a,b) b-b #define pcicfgw16(a,b,c) USED(a);USED(b);USED(c) #define pcicfgr32(a,b) b-b #define pcicfgw32(a,b,c) USED(a);USED(b);USED(c) void incref(Ref*); int decref(Ref*); void kstrdup(char**, char*); void dbgprint(char d, char *fmt, ...); #pragma varargck argpos amlprint 2 #define FLUSH Bflush(bout) extern Biobuf* bout; ustar00nemosys9am/mkfile 644 0 0 2477 11334032355 10553ustar00nemosys /n/sources/contrib/nemo/a.tar} rm -r 9pc 9port echo 9am is exported aml.pdf:D: aml.ps ps2pdf aml.ps >aml.pdf aml.ps:D: aml.ms troff -ms aml.ms | lp -dstdout >aml.ps t:V: broke|rc ; mk 8.out && 8.out $FLAGS acpidump > Out t2:V: broke|rc ; mk 8.out && 8.out $FLAGS acpidump2 > Out2 t3:V: broke|rc ; mk 8.out && 8.out $FLAGS acpidump3 > Out3 t4:V: broke|rc ; mk 8.out && 8.out $FLAGS acpidump4 > Out4 t4:V: broke|rc ; mk 8.out && 8.out $FLAGS acpidump4 > Out4 te:V: broke|rc ; mk 8.out && 8.out $FLAGS acpiella > Outella now that we have configured all apics. */ for(l = apics->st; l != nil; l = l->next) switch(l->type){ case ASintovr: acpictl("int isa %d %d %I", l->intovr.irq, l->intovr.intr, 9am/sargazos.conf.out 664 0 0 0 11321071541 12561ustar00nemosys