1 /*
2 * minimal acpi support for multiprocessors.
3 *
4 * avoids AML but that's only enough to discover
5 * the processors, not the interrupt routing details.
6 */
7 #include "u.h"
8 #include "../port/lib.h"
9 #include "mem.h"
10 #include "dat.h"
11 #include "fns.h"
12 #include "io.h"
13 #include "mp.h"
14 #include "mpacpi.h"
15
16 /* 8c says: out of fixed registers */
17 #define L64GET(p) ((uvlong)L32GET((p)+4) << 32 | L32GET(p))
18
19 enum {
20 /* apic types */
21 Apiclproc,
22 Apicio,
23 Apicintrsrcoverride,
24 Apicnmisrc,
25 Apiclnmi,
26 Apicladdroverride,
27 Apicios,
28 Apicls,
29 Apicintrsrc,
30 Apiclx2,
31 Apiclx2nmi,
32
33 PcmpUsed = 1ul<<31, /* Apic->flags addition */
34
35 Lapicbase = 0x1b, /* msr */
36
37 Lapicae = 1<<11, /* apic enable in Lapicbase */
38 };
39
40 #define dprint(...) if(mpdebug) print(__VA_ARGS__); else USED(mpdebug)
41
42 /* from mp.c */
43 int mpdebug;
44 int mpmachno;
45 Apic mpapic[MaxAPICNO+1];
46 int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */
47
48 Apic *bootapic;
49
50 static int nprocid;
51
52 static uvlong
l64get(uchar * p)53 l64get(uchar *p)
54 {
55 return L64GET(p);
56 }
57
58 int
apicset(Apic * apic,int type,int apicno,int f)59 apicset(Apic *apic, int type, int apicno, int f)
60 {
61 if(apicno > MaxAPICNO)
62 return -1;
63 apic->type = type;
64 apic->apicno = apicno;
65 apic->flags = f | PcmpEN | PcmpUsed;
66 return 0;
67 }
68
69 int
mpnewproc(Apic * apic,int apicno,int f)70 mpnewproc(Apic *apic, int apicno, int f)
71 {
72 if(apic->flags & PcmpUsed) {
73 print("mpnewproc: apic already enabled\n");
74 return -1;
75 }
76 if (apicset(apic, PcmpPROCESSOR, apicno, f) < 0)
77 return -1;
78 apic->lintr[1] = apic->lintr[0] = ApicIMASK;
79 /* botch! just enumerate */
80 if(apic->flags & PcmpBP)
81 apic->machno = 0;
82 else
83 apic->machno = ++mpmachno;
84 machno2apicno[apic->machno] = apicno;
85 return 0;
86 }
87
88 static int
mpacpiproc(uchar * p,ulong laddr)89 mpacpiproc(uchar *p, ulong laddr)
90 {
91 int id, f;
92 ulong *vladdr;
93 vlong base;
94 char *already;
95 Apic *apic;
96
97 /* p bytes: type (0), len (8), cpuid, cpu_lapic id, flags[4] */
98 id = p[3];
99 /* cpu unusable flag or id out of range? */
100 if((L32GET(p+4) & 1) == 0 || id > MaxAPICNO)
101 return -1;
102
103 vladdr = nil;
104 already = "";
105 f = 0;
106 apic = &mpapic[id];
107 dprint("\tmpacpiproc: apic %#p\n", apic);
108 apic->paddr = laddr;
109 if (nprocid++ == 0) {
110 f = PcmpBP;
111 vladdr = vmap(apic->paddr, 1024);
112 if(apic->addr == nil){
113 print("proc apic %d: failed to map %#p\n", id,
114 apic->paddr);
115 already = "(fail)";
116 }
117 bootapic = apic;
118 }
119 apic->addr = vladdr;
120
121 if(apic->flags & PcmpUsed)
122 already = "(on)";
123 else
124 mpnewproc(apic, id, f);
125
126 if (0)
127 dprint("\tapic proc %d/%d apicid %d flags%s%s %s\n", nprocid-1,
128 apic->machno, id, f & PcmpBP? " boot": "",
129 f & PcmpEN? " enabled": "", already);
130 USED(already);
131
132 rdmsr(Lapicbase, &base);
133 if (!(base & Lapicae)) {
134 dprint("mpacpiproc: enabling lapic\n");
135 wrmsr(Lapicbase, base | Lapicae);
136 }
137 return 0;
138 }
139
140 static void
mpacpicpus(Madt * madt)141 mpacpicpus(Madt *madt)
142 {
143 int i, n;
144 ulong laddr;
145 uchar *p;
146
147 laddr = L32GET(madt->addr);
148 dprint("APIC mpacpicpus(%#p) lapic addr %#lux, flags %#ux\n",
149 madt, laddr, L32GET(madt->flags));
150
151 n = L32GET(&madt->sdthdr[4]);
152 p = madt->structures;
153 dprint("\t%d structures at %#p\n",n, p);
154 /* byte 0 is assumed to be type, 1 is assumed to be length */
155 for(i = offsetof(Madt, structures[0]); i < n; i += p[1], p += p[1])
156 switch(p[0]){
157 case Apiclproc:
158 mpacpiproc(p, laddr);
159 break;
160 }
161 }
162
163 /* returns nil iff checksum is bad */
164 static void *
mpacpirsdchecksum(void * addr,int length)165 mpacpirsdchecksum(void* addr, int length)
166 {
167 uchar *p, sum;
168
169 sum = 0;
170 for(p = addr; length-- > 0; p++)
171 sum += *p;
172 return sum == 0? addr: nil;
173 }
174
175 /* call func for each acpi table found */
176 static void
mpacpiscan(void (* func)(uchar *))177 mpacpiscan(void (*func)(uchar *))
178 {
179 int asize, i, tbllen, sdtlen;
180 uintptr dhpa, sdtpa;
181 uchar *tbl, *sdt;
182 Rsd *rsd;
183
184 dprint("ACPI...");
185 if((rsd = sigsearch("RSD PTR ")) == nil) {
186 dprint("none\n");
187 return;
188 }
189
190 dprint("rsd %#p physaddr %#ux length %ud %#llux rev %d oem %.6s\n",
191 rsd, L32GET(rsd->raddr), L32GET(rsd->length),
192 l64get(rsd->xaddr), rsd->revision, (char*)rsd->oemid);
193
194 if(rsd->revision == 2){
195 if(mpacpirsdchecksum(rsd, 36) == nil)
196 return;
197 asize = 8;
198 sdtpa = l64get(rsd->xaddr);
199 } else {
200 if(mpacpirsdchecksum(rsd, 20) == nil)
201 return;
202 asize = 4;
203 sdtpa = L32GET(rsd->raddr);
204 }
205
206 if((sdt = vmap(sdtpa, 8)) == nil)
207 return;
208 if((sdt[0] != 'R' && sdt[0] != 'X') || memcmp(sdt+1, "SDT", 3) != 0){
209 vunmap(sdt, 8);
210 return;
211 }
212 sdtlen = L32GET(sdt + 4);
213 vunmap(sdt, 8);
214
215 if((sdt = vmap(sdtpa, sdtlen)) == nil)
216 return;
217 if(mpacpirsdchecksum(sdt, sdtlen) != nil)
218 for(i = 36; i < sdtlen; i += asize){
219 if(asize == 8)
220 dhpa = l64get(sdt+i);
221 else
222 dhpa = L32GET(sdt+i);
223
224 if((tbl = vmap(dhpa, 8)) == nil)
225 continue;
226 tbllen = L32GET(tbl + 4);
227 vunmap(tbl, 8);
228
229 if((tbl = vmap(dhpa, tbllen)) == nil)
230 continue;
231 if(mpacpirsdchecksum(tbl, tbllen) != nil)
232 (*func)(tbl);
233 vunmap(tbl, tbllen);
234 }
235 vunmap(sdt, sdtlen);
236 }
237
238 static void
mpacpitbl(uchar * p)239 mpacpitbl(uchar *p)
240 {
241 /* for now, just activate any idle cpus */
242 if (memcmp(p, "APIC", 4) == 0)
243 mpacpicpus((Madt *)p);
244 }
245
246 static void
mpacpi(void)247 mpacpi(void)
248 {
249 mpdebug = getconf("*debugmp") != nil;
250 mpacpiscan(mpacpitbl);
251 }
252
253 void (*mpacpifunc)(void) = mpacpi;
254