xref: /plan9/sys/src/9/pc/mpacpi.c (revision 00a4193c1e0554905f959f11961389c706f63ac9)
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