]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/archacpi.c
archacpi: make *acpi=1 the default
[plan9front.git] / sys / src / 9 / pc / archacpi.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/pci.h"
8 #include "../port/error.h"
9
10 #include "mp.h"
11
12 #include <aml.h>
13
14 typedef struct Rsd Rsd;
15 typedef struct Tbl Tbl;
16
17 struct Rsd {
18         uchar   sig[8];
19         uchar   csum;
20         uchar   oemid[6];
21         uchar   rev;
22         uchar   raddr[4];
23         uchar   len[4];
24         uchar   xaddr[8];
25         uchar   xcsum;
26         uchar   reserved[3];
27 };
28
29 struct Tbl {
30         uchar   sig[4];
31         uchar   len[4];
32         uchar   rev;
33         uchar   csum;
34         uchar   oemid[6];
35         uchar   oemtid[8];
36         uchar   oemrev[4];
37         uchar   cid[4];
38         uchar   crev[4];
39         uchar   data[];
40 };
41
42 enum {
43         Tblsz   = 4+4+1+1+6+8+4+4+4,
44 };
45
46 static Rsd *rsd;
47
48 /* physical addresses visited by maptable() */
49 static int ntblpa;
50 static uvlong tblpa[64];
51
52 /* successfully mapped tables */
53 static int ntblmap;
54 static Tbl *tblmap[64];
55
56 static ushort
57 get16(uchar *p){
58         return p[1]<<8 | p[0];
59 }
60
61 static uint
62 get32(uchar *p){
63         return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
64 }
65
66 static uvlong
67 get64(uchar *p){
68         uvlong u;
69
70         u = get32(p+4);
71         return u<<32 | get32(p);
72 }
73
74 static uint
75 tbldlen(Tbl *t){
76         return get32(t->len) - Tblsz;
77 }
78
79 static long
80 memcheck(uintptr pa, long len)
81 {
82         int i;
83         uintptr pe;
84         Confmem *cm;
85
86         if(len <= 0)
87                 return len;
88         pe = pa + len-1;
89         if(pe < pa){
90                 len = -pa;
91                 pe = pa + len-1;
92         }
93         if(pa < PADDR(CPU0END))
94                 return 0;
95         if(pe >= PADDR(KTZERO) && pa < PADDR(end))
96                 return PADDR(KTZERO) - pa;
97         for(i=0; i<nelem(conf.mem); i++){
98                 cm = &conf.mem[i];
99                 if(cm->npage == 0)
100                         continue;
101                 if(pe >= cm->base && pa <= cm->base + cm->npage*BY2PG - 1)
102                         return cm->base - pa;
103         }
104         return len;
105 }
106
107 static void
108 maptable(uvlong pa)
109 {
110         uchar *p, *e;
111         u32int l;
112         Tbl *t;
113         int i;
114
115         if(-pa < 8)
116                 return;
117
118         if(ntblpa >= nelem(tblpa) || ntblmap >= nelem(tblmap))
119                 return;
120
121         for(i=0; i<ntblpa; i++){
122                 if(pa == tblpa[i])
123                         return;
124         }
125         tblpa[ntblpa++] = pa;
126
127         memreserve(pa, 8);
128         if((t = vmap(pa, 8)) == nil)
129                 return;
130         l = get32(t->len);
131         if(l < Tblsz
132         || l >= 0x10000000
133         || -pa < l){
134                 vunmap(t, 8);
135                 return;
136         }
137         memreserve(pa, l);
138         vunmap(t, 8);
139         if((t = vmap(pa, l)) == nil)
140                 return;
141         if(checksum(t, l)){
142                 vunmap(t, l);
143                 return;
144         }
145         tblmap[ntblmap++] = t;
146
147         p = (uchar*)t;
148         e = p + l;
149         if(memcmp("RSDT", t->sig, 4) == 0){
150                 for(p = t->data; p+3 < e; p += 4)
151                         maptable(get32(p));
152                 return;
153         }
154         if(memcmp("XSDT", t->sig, 4) == 0){
155                 for(p = t->data; p+7 < e; p += 8)
156                         maptable(get64(p));
157                 return;
158         }
159         if(memcmp("FACP", t->sig, 4) == 0){
160                 if(l < 44)
161                         return;
162                 maptable(get32(p + 40));
163                 if(l < 148)
164                         return;
165                 maptable(get64(p + 140));
166                 return;
167         }
168 }
169
170 static void
171 maptables(void)
172 {
173         if(rsd == nil || ntblmap > 0 || ntblpa > 0)
174                 return;
175         if(!checksum(rsd, 20))
176                 maptable(get32(rsd->raddr));
177         if(rsd->rev >= 2){
178                 if(!checksum(rsd, 36))
179                         maptable(get64(rsd->xaddr));
180         }
181 }
182
183 static Tbl*
184 findtable(char sig[4])
185 {
186         Tbl *t;
187         int i;
188
189         for(i=0; i<ntblmap; i++){
190                 t = tblmap[i];
191                 if(memcmp(t->sig, sig, 4) == 0)
192                         return t;
193         }
194         return nil;
195 }
196
197 static Apic*
198 findapic(int gsi, int *pintin)
199 {
200         Apic *a;
201         int i;
202
203         for(i=0; i<=MaxAPICNO; i++){
204                 if((a = mpioapic[i]) == nil)
205                         continue;
206                 if((a->flags & PcmpEN) == 0)
207                         continue;
208                 if(gsi >= a->gsibase && gsi <= a->gsibase+a->mre){
209                         if(pintin)
210                                 *pintin = gsi - a->gsibase;
211                         return a;
212                 }
213         }
214         print("findapic: no ioapic found for gsi %d\n", gsi);
215         return nil;
216 }
217
218 static void
219 addirq(int gsi, int type, int busno, int irq, int flags)
220 {
221         Apic *a;
222         Bus *bus;
223         Aintr *ai;
224         PCMPintr *pi;
225         int intin;
226
227         if((a = findapic(gsi, &intin)) == nil)
228                 return;
229
230         for(bus = mpbus; bus; bus = bus->next)
231                 if(bus->type == type && bus->busno == busno)
232                         goto Foundbus;
233
234         if((bus = xalloc(sizeof(Bus))) == nil)
235                 panic("addirq: no memory for Bus");
236         bus->busno = busno;
237         bus->type = type;
238         if(type == BusISA){
239                 bus->po = PcmpHIGH;
240                 bus->el = PcmpEDGE;
241                 if(mpisabus == -1)
242                         mpisabus = busno;
243         } else {
244                 bus->po = PcmpLOW;
245                 bus->el = PcmpLEVEL;
246         }
247         if(mpbus)
248                 mpbuslast->next = bus;
249         else
250                 mpbus = bus;
251         mpbuslast = bus;
252
253 Foundbus:
254         for(ai = bus->aintr; ai; ai = ai->next)
255                 if(ai->intr->irq == irq)
256                         return;
257
258         if((pi = xalloc(sizeof(PCMPintr))) == nil)
259                 panic("addirq: no memory for PCMPintr");
260         pi->type = PcmpIOINTR;
261         pi->intr = PcmpINT;
262         pi->flags = flags & (PcmpPOMASK|PcmpELMASK);
263         pi->busno = busno;
264         pi->irq = irq;
265         pi->apicno = a->apicno;
266         pi->intin = intin;
267
268         if((ai = xalloc(sizeof(Aintr))) == nil)
269                 panic("addirq: no memory for Aintr");
270         ai->intr = pi;
271         ai->apic = a;
272         ai->next = bus->aintr;
273         ai->bus = bus;
274         bus->aintr = ai;
275 }
276
277 static char*
278 eisaid(void *v)
279 {
280         static char id[8];
281         ulong b, l;
282         int i;
283
284         if(amltag(v) == 's')
285                 return v;
286         b = amlint(v);
287         for(l = 0, i=24; i>=0; i -= 8, b >>= 8)
288                 l |= (b & 0xFF) << i;
289         id[7] = 0;
290         for(i=6; i>=3; i--, l >>= 4)
291                 id[i] = "0123456789ABCDEF"[l & 0xF];
292         for(i=2; i>=0; i--, l >>= 5)
293                 id[i] = '@' + (l & 0x1F);
294         return id;
295 }
296
297 static int
298 pcibusno(void *dot)
299 {
300         int bno, adr, tbdf;
301         Pcidev *pdev;
302         void *p, *x;
303         char *id;
304
305         id = nil;
306         if((x = amlwalk(dot, "^_HID")) != nil)
307                 if((p = amlval(x)) != nil)
308                         id = eisaid(p);
309         if((x = amlwalk(dot, "^_BBN")) == nil)
310                 if((x = amlwalk(dot, "^_ADR")) == nil)
311                         return -1;
312         p = nil;
313         if(amleval(x, "", &p) < 0)
314                 return -1;
315         adr = amlint(p);
316         /* if root bridge, then we are done here */
317         if(id != nil && (strcmp(id, "PNP0A03")==0 || strcmp(id, "PNP0A08")==0))
318                 return adr;
319         x = amlwalk(dot, "^");
320         if(x == nil || x == dot)
321                 return -1;
322         if((bno = pcibusno(x)) < 0)
323                 return -1;
324         tbdf = MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
325         pdev = pcimatchtbdf(tbdf);
326         if(pdev == nil)
327                 return -1;
328         if(pdev->bridge == nil)
329                 return bno;
330         return BUSBNO(pdev->bridge->tbdf);
331 }
332
333 static int
334 pciaddr(void *dot)
335 {
336         int adr, bno;
337         void *x, *p;
338
339         for(;;){
340                 if((x = amlwalk(dot, "_ADR")) == nil){
341                         x = amlwalk(dot, "^");
342                         if(x == nil || x == dot)
343                                 break;
344                         dot = x;
345                         continue;
346                 }
347                 if((bno = pcibusno(x)) < 0)
348                         break;
349                 p = nil;
350                 if(amleval(x, "", &p) < 0)
351                         break;
352                 adr = amlint(p);
353                 return MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
354         }
355         return -1;
356 }
357
358 static int
359 getirqs(void *d, uchar pmask[32], int *pflags)
360 {
361         int i, n, m;
362         uchar *p;
363
364         *pflags = 0;
365         memset(pmask, 0, 32);
366         if(amltag(d) != 'b')
367                 return -1;
368         p = amlval(d);
369         if(amllen(d) >= 2 && (p[0] == 0x22 || p[0] == 0x23)){
370                 pmask[0] = p[1];
371                 pmask[1] = p[2];
372                 if(amllen(d) >= 3 && p[0] == 0x23)
373                         *pflags = ((p[3] & (1<<0)) ? PcmpEDGE : PcmpLEVEL)
374                                 | ((p[3] & (1<<3)) ? PcmpLOW : PcmpHIGH);
375                 return 0;
376         }
377         if(amllen(d) >= 5 && p[0] == 0x89){
378                 n = p[4];
379                 if(amllen(d) < 5+n*4)
380                         return -1;
381                 for(i=0; i<n; i++){
382                         m = get32(p+5 + i*4);
383                         if(m >= 0 && m < 256)
384                                 pmask[m/8] |= 1<<(m%8);
385                 }
386                 *pflags = ((p[3] & (1<<1)) ? PcmpEDGE : PcmpLEVEL)
387                         | ((p[3] & (1<<2)) ? PcmpLOW : PcmpHIGH);
388                 return 0;
389         }
390         return -1;
391 }
392
393 static uchar*
394 setirq(void *d, uint irq)
395 {
396         uchar *p;
397
398         if(amltag(d) != 'b')
399                 return nil;
400         p = amlnew('b', amllen(d));
401         memmove(p, d, amllen(p));
402         if(p[0] == 0x22 || p[0] == 0x23){
403                 irq = 1<<irq;
404                 p[1] = irq;
405                 p[2] = irq>>8;
406         }
407         if(p[0] == 0x89){
408                 p[4] = 1;
409                 p[5] = irq;
410                 p[6] = irq>>8;
411                 p[7] = irq>>16;
412                 p[8] = irq>>24;
413         }
414         return p;
415 }
416
417 static int
418 setuplink(void *link, int *pflags)
419 {
420         uchar im, pm[32], cm[32], *c;
421         static int lastirq = 1;
422         int gsi, i;
423         void *r;
424
425         if(amltag(link) != 'N')
426                 return -1;
427
428         r = nil;
429         if(amleval(amlwalk(link, "_PRS"), "", &r) < 0)
430                 return -1;
431         if(getirqs(r, pm, pflags) < 0)
432                 return -1;
433
434         r = nil;
435         if(amleval(amlwalk(link, "_CRS"), "", &r) < 0)
436                 return -1;
437         if(getirqs(r, cm, pflags) < 0)
438                 return -1;
439         
440         gsi = -1;
441         for(i=0; i<256; i++){
442                 im = 1<<(i%8);
443                 if(pm[i/8] & im){
444                         if(cm[i/8] & im)
445                                 gsi = i;
446                 }
447         }
448
449         if(gsi > 0 || getconf("*nopcirouting") != nil)
450                 return gsi;
451
452         for(i=0; i<256; i++){
453                 gsi = lastirq++ & 0xFF; /* round robin */
454                 im = 1<<(gsi%8);
455                 if(pm[gsi/8] & im){
456                         if((c = setirq(r, gsi)) == nil)
457                                 break;
458                         if(amleval(amlwalk(link, "_SRS"), "b", c, nil) < 0)
459                                 break;
460                         return gsi;
461                 }
462         }
463         return -1;
464 }
465
466 static int
467 enumprt(void *dot, void *)
468 {
469         void *p, **a, **b;
470         int bno, dno, pin, gsi, flags;
471         int n, i;
472
473         bno = pcibusno(dot);
474         if(bno < 0)
475                 return 1;
476
477         /* evalulate _PRT method */
478         p = nil;
479         if(amleval(dot, "", &p) < 0)
480                 return 1;
481         if(amltag(p) != 'p')
482                 return 1;
483
484         amltake(p);
485         n = amllen(p);
486         a = amlval(p);
487         for(i=0; i<n; i++){
488                 if(amltag(a[i]) != 'p')
489                         continue;
490                 if(amllen(a[i]) != 4)
491                         continue;
492                 flags = 0;
493                 b = amlval(a[i]);
494                 dno = amlint(b[0])>>16;
495                 pin = amlint(b[1]);
496                 gsi = amlint(b[3]);
497                 if(gsi==0){
498                         gsi = setuplink(b[2], &flags);
499                         if(gsi <= 0)
500                                 continue;
501                 }
502                 addirq(gsi, BusPCI, bno, (dno<<2)|pin, flags);
503         }
504         amldrop(p);
505
506         return 1;
507 }
508
509 static int
510 enumec(void *dot, void *)
511 {
512         int cmdport, dataport;
513         uchar *b;
514         void *x;
515         char *id;
516
517         b = nil;
518         id = eisaid(amlval(amlwalk(dot, "^_HID")));
519         if(id == nil || strcmp(id, "PNP0C09") != 0)
520                 return 1;
521         if((x = amlwalk(dot, "^_CRS")) == nil)
522                 return 1;
523         if(amleval(x, "", &b) < 0 || amltag(b) != 'b' || amllen(b) < 16)
524                 return 1;
525         if(b[0] != 0x47 || b[8] != 0x47)        /* two i/o port descriptors */
526                 return 1;
527         dataport = b[0+2] | b[0+3]<<8;
528         cmdport = b[8+2] | b[8+3]<<8;
529         ecinit(cmdport, dataport);
530         return 1;
531 }
532
533 static long
534 readmem(Chan*, void *v, long n, vlong o)
535 {
536         uvlong pa = (uvlong)o;
537         void *t;
538
539         if((n = memcheck(pa, n)) <= 0)
540                 return 0;
541         if((t = vmap(pa, n)) == nil)
542                 error(Enovmem);
543         if(waserror()){
544                 vunmap(t, n);
545                 nexterror();
546         }
547         memmove(v, t, n);
548         vunmap(t, n);
549         poperror();
550         return n;
551 }
552
553 static long
554 writemem(Chan*, void *v, long n, vlong o)
555 {
556         uvlong pa = (uvlong)o;
557         void *t;
558
559         if(memcheck(pa, n) != n)
560                 error(Eio);
561         if((t = vmap(pa, n)) == nil)
562                 error(Enovmem);
563         if(waserror()){
564                 vunmap(t, n);
565                 nexterror();
566         }
567         memmove(t, v, n);
568         vunmap(t, n);
569         poperror();
570         return n;
571 }
572
573 static void
574 acpiinit(void)
575 {
576         Tbl *t;
577         Apic *a;
578         void *va;
579         uchar *s, *p, *e;
580         ulong lapicbase;
581         int machno, i, c;
582
583         amlinit();
584
585         /* load DSDT */
586         if((t = findtable("DSDT")) != nil){
587                 amlintmask = (~0ULL) >> (t->rev <= 1)*32;
588                 amlload(t->data, tbldlen(t));
589         }
590
591         /* load SSDT, there can be multiple tables */
592         for(i=0; i<ntblmap; i++){
593                 t = tblmap[i];
594                 if(memcmp(t->sig, "SSDT", 4) == 0)
595                         amlload(t->data, tbldlen(t));
596         }
597
598         /* set APIC mode */
599         amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil);
600
601         t = findtable("APIC");
602         if(t == nil)
603                 panic("acpiinit: no MADT (APIC) table");
604
605         s = t->data;
606         e = s + tbldlen(t);
607         lapicbase = get32(s); s += 8;
608         va = vmap(lapicbase, 1024);
609         print("LAPIC: %.8lux %#p\n", lapicbase, va);
610         if(va == nil)
611                 panic("acpiinit: cannot map lapic %.8lux", lapicbase);
612
613         machno = 0;
614         for(p = s; p < e; p += c){
615                 c = p[1];
616                 if(c < 2 || (p+c) > e)
617                         break;
618                 switch(*p){
619                 case 0x00:      /* Processor Local APIC */
620                         if(p[3] > MaxAPICNO)
621                                 break;
622                         if((a = xalloc(sizeof(Apic))) == nil)
623                                 panic("acpiinit: no memory for Apic");
624                         a->type = PcmpPROCESSOR;
625                         a->apicno = p[3];
626                         a->paddr = lapicbase;
627                         a->addr = va;
628                         a->lintr[0] = ApicIMASK;
629                         a->lintr[1] = ApicIMASK;
630                         a->flags = p[4] & PcmpEN;
631
632                         /* skip disabled processors */
633                         if((a->flags & PcmpEN) == 0 || mpapic[a->apicno] != nil){
634                                 xfree(a);
635                                 break;
636                         }
637                         a->machno = machno++;
638
639                         /*
640                          * platform firmware should list the boot processor
641                          * as the first processor entry in the MADT
642                          */
643                         if(a->machno == 0)
644                                 a->flags |= PcmpBP;
645
646                         mpapic[a->apicno] = a;
647                         break;
648                 case 0x01:      /* I/O APIC */
649                         if(p[2] > MaxAPICNO)
650                                 break;
651                         if((a = xalloc(sizeof(Apic))) == nil)
652                                 panic("acpiinit: no memory for io Apic");
653                         a->type = PcmpIOAPIC;
654                         a->apicno = p[2];
655                         a->paddr = get32(p+4);
656                         if((a->addr = vmap(a->paddr, 1024)) == nil)
657                                 panic("acpiinit: cannot map ioapic %.8lux", a->paddr);
658                         a->gsibase = get32(p+8);
659                         a->flags = PcmpEN;
660                         mpioapic[a->apicno] = a;
661                         ioapicinit(a, a->apicno);
662                         break;
663                 }
664         }
665
666         /*
667          * need 2nd pass as vbox puts interrupt overrides
668          * *before* the ioapic entries (!)
669          */
670         for(p = s; p < e; p += c){
671                 c = p[1];
672                 if(c < 2 || (p+c) > e)
673                         break;
674                 switch(*p){
675                 case 0x02:      /* Interrupt Source Override */
676                         addirq(get32(p+4), BusISA, 0, p[3], get16(p+8));
677                         break;
678                 case 0x03:      /* NMI Source */
679                 case 0x04:      /* Local APIC NMI */
680                 case 0x05:      /* Local APIC Address Override */
681                 case 0x06:      /* I/O SAPIC */
682                 case 0x07:      /* Local SAPIC */
683                 case 0x08:      /* Platform Interrupt Sources */
684                 case 0x09:      /* Processor Local x2APIC */
685                 case 0x0A:      /* x2APIC NMI */
686                 case 0x0B:      /* GIC */
687                 case 0x0C:      /* GICD */
688                         break;
689                 }
690         }
691
692         /* find embedded controller */
693         amlenum(amlroot, "_HID", enumec, nil);
694
695         /* look for PCI interrupt mappings */
696         amlenum(amlroot, "_PRT", enumprt, nil);
697
698         /* add identity mapped legacy isa interrupts */
699         for(i=0; i<16; i++)
700                 addirq(i, BusISA, 0, i, 0);
701
702         /* free the AML interpreter */
703         amlexit();
704
705         /*
706          * Ininitalize local APIC and start application processors.
707          */
708         mpinit();
709 }
710
711 static void
712 acpireset(void)
713 {
714         uchar *p;
715         Tbl *t;
716
717         /* stop application processors */
718         mpshutdown();
719
720         /* locate and write platform reset register */
721         while((t = findtable("FACP")) != nil){
722                 if(get32(t->len) <= 128)
723                         break;
724                 p = (uchar*)t;
725                 if((get32(p + 112) & (1<<10)) == 0)
726                         break;
727                 if(p[116+0] != IoSpace)
728                         break;
729                 outb(get32(p+116+4), p[128]);
730                 break;
731         }
732
733         /* acpi shutdown failed, try generic reset */
734         archreset();
735 }
736
737 static int identify(void);
738 extern int i8259irqno(int, int);
739 extern void i8253init(void);
740
741 extern int hpetprobe(uvlong);
742 extern void hpetinit(void);
743 extern uvlong hpetread(uvlong*);
744
745 PCArch archacpi = {
746 .id=            "ACPI", 
747 .ident=         identify,
748 .reset=         acpireset,
749 .intrinit=      acpiinit,
750 .intrassign=    mpintrassign,
751 .intrirqno=     i8259irqno,
752 .intron=        lapicintron,
753 .introff=       lapicintroff,
754 .clockinit=     i8253init,
755 .fastclock=     i8253read,
756 .timerset=      lapictimerset,
757 };
758
759 static long
760 readtbls(Chan*, void *v, long n, vlong o)
761 {
762         int i, l, m;
763         uchar *p;
764         Tbl *t;
765
766         maptables();
767
768         p = v;
769         for(i=0; n > 0 && i < ntblmap; i++){
770                 t = tblmap[i];
771                 l = get32(t->len);
772                 if(o >= l){
773                         o -= l;
774                         continue;
775                 }
776                 m = l - o;
777                 if(m > n)
778                         m = n;
779                 memmove(p, (uchar*)t + o, m);
780                 p += m;
781                 n -= m;
782                 o = 0;
783         }
784         return p - (uchar*)v;
785 }
786
787 static int
788 identify(void)
789 {
790         uvlong v;
791         char *cp;
792         Tbl *t;
793
794         if((cp = getconf("*acpi")) == nil)
795                 cp = "1";       /* search for rsd by default */
796         v = (uintptr)strtoull(cp, nil, 16);
797         if(v <= 1)
798                 rsd = rsdsearch();
799         else {
800                 memreserve(v, sizeof(Rsd));
801                 rsd = vmap(v, sizeof(Rsd));
802         }
803         if(rsd == nil)
804                 return 1;
805         if(checksum(rsd, 20) && checksum(rsd, 36))
806                 return 1;
807         maptables();
808         addarchfile("acpitbls", 0444, readtbls, nil);
809         addarchfile("acpimem", 0600, readmem, writemem);
810         if(v == 0 || findtable("APIC") == nil)
811                 return 1;
812         if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
813                 return 1;
814         if(getconf("*nohpet") == nil
815         && (t = findtable("HPET")) != nil
816         && ((uchar*)t)[40] == 0
817         && hpetprobe(get64((uchar*)t+44)) == 0){
818                 archacpi.clockinit = hpetinit;
819                 archacpi.fastclock = hpetread;
820         }
821         if(m->havetsc && getconf("*notsc") == nil)
822                 archacpi.fastclock = tscticks;
823
824         return 0;
825 }
826
827 static int
828 readpcicfg(Amlio *io, void *data, int n, int offset)
829 {
830         ulong r, x;
831         Pcidev *p;
832         uchar *a;
833         int i;
834
835         a = data;
836         p = io->aux;
837         if(p == nil)
838                 return -1;
839         offset += io->off;
840         if(offset > 256)
841                 return 0;
842         if(n+offset > 256)
843                 n = 256-offset;
844         r = offset;
845         if(!(r & 3) && n == 4){
846                 x = pcicfgr32(p, r);
847                 PBIT32(a, x);
848                 return 4;
849         }
850         if(!(r & 1) && n == 2){
851                 x = pcicfgr16(p, r);
852                 PBIT16(a, x);
853                 return 2;
854         }
855         for(i = 0; i <  n; i++){
856                 x = pcicfgr8(p, r);
857                 PBIT8(a, x);
858                 a++;
859                 r++;
860         }
861         return i;
862 }
863
864 static int
865 readec(Amlio *io, void *data, int n, int off)
866 {
867         int port, v;
868         uchar *p;
869
870         USED(io);
871         if(off < 0 || off >= 256)
872                 return 0;
873         if(off+n > 256)
874                 n = 256 - off;
875         p = data;
876         for(port = off; port < off+n; port++){
877                 if((v = ecread(port)) < 0)
878                         break;
879                 *p++ = v;
880         }
881         return n;
882 }
883
884 static int
885 writeec(Amlio *io, void *data, int n, int off)
886 {
887         int port;
888         uchar *p;
889
890         USED(io);
891         if(off < 0 || off+n > 256)
892                 return -1;
893         p = data;
894         for(port = off; port < off+n; port++)
895                 if(ecwrite(port, *p++) < 0)
896                         break;
897         return n;
898 }
899
900 static int
901 writepcicfg(Amlio *io, void *data, int n, int offset)
902 {
903         ulong r, x;
904         Pcidev *p;
905         uchar *a;
906         int i;
907
908         a = data;
909         p = io->aux;
910         if(p == nil)
911                 return -1;
912         offset += io->off;
913         if(offset > 256)
914                 return 0;
915         if(n+offset > 256)
916                 n = 256-offset;
917         r = offset;
918         if(!(r & 3) && n == 4){
919                 x = GBIT32(a);
920                 pcicfgw32(p, r, x);
921                 return 4;
922         }
923         if(!(r & 1) && n == 2){
924                 x = GBIT16(a);
925                 pcicfgw16(p, r, x);
926                 return 2;
927         }
928         for(i = 0; i <  n; i++){
929                 x = GBIT8(a);
930                 pcicfgw8(p, r, x);
931                 a++;
932                 r++;
933         }
934         return i;
935 }
936
937 static int
938 readioport(Amlio *io, void *data, int len, int port)
939 {
940         uchar *a;
941
942         a = data;
943         port += io->off;
944         switch(len){
945         case 4:
946                 PBIT32(a, inl(port));
947                 return 4;
948         case 2:
949                 PBIT16(a, ins(port));
950                 return 2;
951         case 1:
952                 PBIT8(a, inb(port));
953                 return 1;
954         }
955         return -1;
956 }
957
958 static int
959 writeioport(Amlio *io, void *data, int len, int port)
960 {
961         uchar *a;
962
963         a = data;
964         port += io->off;
965         switch(len){
966         case 4:
967                 outl(port, GBIT32(a));
968                 return 4;
969         case 2:
970                 outs(port, GBIT16(a));
971                 return 2;
972         case 1:
973                 outb(port, GBIT8(a));
974                 return 1;
975         }
976         return -1;
977 }
978
979 int
980 amlmapio(Amlio *io)
981 {
982         int tbdf;
983         Pcidev *pdev;
984         char buf[64];
985
986         switch(io->space){
987         default:
988                 print("amlmapio: address space %x not implemented\n", io->space);
989                 break;
990         case MemSpace:
991                 if(memcheck(io->off, io->len) != io->len){
992                         print("amlmapio: [%#p-%#p) overlaps usable memory\n",
993                                 (uintptr)io->off, (uintptr)(io->off+io->len));
994                         break;
995                 }
996                 if((io->va = vmap(io->off, io->len)) == nil){
997                         print("amlmapio: vmap failed\n");
998                         break;
999                 }
1000                 return 0;
1001         case IoSpace:
1002                 snprint(buf, sizeof(buf), "%N", io->name);
1003                 if(ioalloc(io->off, io->len, 0, buf) < 0){
1004                         print("amlmapio: ioalloc failed\n");
1005                         break;
1006                 }
1007                 io->read = readioport;
1008                 io->write = writeioport;
1009                 return 0;
1010         case PcicfgSpace:
1011                 if((tbdf = pciaddr(io->name)) < 0){
1012                         print("amlmapio: no address\n");
1013                         break;
1014                 }
1015                 if((pdev = pcimatchtbdf(tbdf)) == nil){
1016                         print("amlmapio: no device %T\n", tbdf);
1017                         break;
1018                 }
1019                 io->aux = pdev;
1020                 io->read = readpcicfg;
1021                 io->write = writepcicfg;
1022                 return 0;
1023         case EbctlSpace:
1024                 io->read = readec;
1025                 io->write = writeec;
1026                 return 0;
1027         }
1028         print("amlmapio: mapping %N failed\n", io->name);
1029         return -1;
1030 }
1031
1032 void
1033 amlunmapio(Amlio *io)
1034 {
1035         switch(io->space){
1036         case MemSpace:
1037                 vunmap(io->va, io->len);
1038                 break;
1039         case IoSpace:
1040                 iofree(io->off);
1041                 break;
1042         }
1043 }
1044
1045 void*
1046 amlalloc(int n){
1047         void *p;
1048
1049         if((p = malloc(n)) == nil)
1050                 panic("amlalloc: no memory");
1051         memset(p, 0, n);
1052         return p;
1053 }
1054
1055 void
1056 amlfree(void *p){
1057         free(p);
1058 }
1059
1060 void
1061 amldelay(int us)
1062 {
1063         microdelay(us);
1064 }