xref: /plan9-contrib/sys/src/9k/k10/mp.c (revision 59461365a183d2654b8e210a706556113b1536a9)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 #include "io.h"
8 #include "apic.h"
9 #include "mp.h"
10 
11 /*
12  * MultiProcessor Specification Version 1.[14].
13  */
14 typedef struct {				/* MP Floating Pointer */
15 	u8int	signature[4];			/* "_MP_" */
16 	u8int	addr[4];			/* PCMP */
17 	u8int	length;				/* 1 */
18 	u8int	revision;			/* [14] */
19 	u8int	checksum;
20 	u8int	feature[5];
21 } _MP_;
22 
23 typedef struct {				/* MP Configuration Table */
24 	u8int	signature[4];			/* "PCMP" */
25 	u8int	length[2];
26 	u8int	revision;			/* [14] */
27 	u8int	checksum;
28 	u8int	string[20];			/* OEM + Product ID */
29 	u8int	oaddr[4];			/* OEM table pointer */
30 	u8int	olength[2];			/* OEM table length */
31 	u8int	entry[2];			/* entry count */
32 	u8int	apicpa[4];			/* local APIC address */
33 	u8int	xlength[2];			/* extended table length */
34 	u8int	xchecksum;			/* extended table checksum */
35 	u8int	reserved;
36 
37 	u8int	entries[];
38 } PCMP;
39 
40 typedef struct {
41 	char	type[6];
42 	int	polarity;			/* default for this bus */
43 	int	trigger;			/* default for this bus */
44 } Mpbus;
45 
46 static Mpbus mpbusdef[] = {
47 	{ "PCI   ", IPlow, TMlevel, },
48 	{ "ISA   ", IPhigh, TMedge, },
49 };
50 static Mpbus* mpbus[Nbus];
51 int mpisabusno = -1;
52 
53 static void
mpintrprint(char * s,u8int * p)54 mpintrprint(char* s, u8int* p)
55 {
56 	char buf[128], *b, *e;
57 	char format[] = " type %d flags %#ux bus %d IRQ %d APIC %d INTIN %d\n";
58 
59 	b = buf;
60 	e = b + sizeof(buf);
61 	b = seprint(b, e, "mpparse: intr:");
62 	if(s != nil)
63 		b = seprint(b, e, " %s:", s);
64 	seprint(b, e, format, p[1], l16get(p+2), p[4], p[5], p[6], p[7]);
65 	print(buf);
66 }
67 
68 static u32int
mpmkintr(u8int * p)69 mpmkintr(u8int* p)
70 {
71 	u32int v;
72 	Apic *apic;
73 	int n, polarity, trigger;
74 
75 	/*
76 	 * Check valid bus, interrupt input pin polarity
77 	 * and trigger mode. If the APIC ID is 0xff it means
78 	 * all APICs of this type so those checks for useable
79 	 * APIC and valid INTIN must also be done later in
80 	 * the appropriate init routine in that case. It's hard
81 	 * to imagine routing a signal to all IOAPICs, the
82 	 * usual case is routing NMI and ExtINT to all LAPICs.
83 	 */
84 	if(mpbus[p[4]] == nil){
85 		mpintrprint("no source bus", p);
86 		return 0;
87 	}
88 	if(p[6] != 0xff){
89 		if(Napic < 256 && p[6] >= Napic){
90 			mpintrprint("APIC ID out of range", p);
91 			return 0;
92 		}
93 		switch(p[0]){
94 		default:
95 			mpintrprint("INTIN botch", p);
96 			return 0;
97 		case PcmpIOINTR:
98 			apic = &ioapic[p[6]];
99 			if(!apic->useable){
100 				mpintrprint("unuseable APIC", p);
101 				return 0;
102 			}
103 			if(p[7] >= apic->nrdt){
104 				mpintrprint("IO INTIN out of range", p);
105 				return 0;
106 			}
107 			break;
108 		case PcmpLINTR:
109 			apic = &xapic[p[6]];
110 			if(!apic->useable){
111 				mpintrprint("unuseable APIC", p);
112 				return 0;
113 			}
114 			if(p[7] >= nelem(apic->lvt)){
115 				mpintrprint("LOCAL INTIN out of range", p);
116 				return 0;
117 			}
118 			break;
119 		}
120 	}
121 	n = l16get(p+2);
122 	if((polarity = (n & PcmpPOMASK)) == 2 || (trigger = ((n>>2) & PcmpPOMASK)) == 2){
123 		mpintrprint("invalid polarity/trigger", p);
124 		return 0;
125 	}
126 
127 	/*
128 	 * Create the low half of the vector table entry (LVT or RDT).
129 	 * For the NMI, SMI and ExtINT cases, the polarity and trigger
130 	 * are fixed (but are not always consistent over IA-32 generations).
131 	 * For the INT case, either the polarity/trigger are given or
132 	 * it defaults to that of the source bus;
133 	 * whether INT is Fixed or Lowest Priority is left until later.
134 	 */
135 	v = Im;
136 	switch(p[1]){
137 	default:
138 		mpintrprint("invalid type", p);
139 		return 0;
140 	case PcmpINT:
141 		switch(polarity){
142 		case 0:
143 			v |= mpbus[p[4]]->polarity;
144 			break;
145 		case PcmpHIGH:
146 			v |= IPhigh;
147 			break;
148 		case PcmpLOW:
149 			v |= IPlow;
150 			break;
151 		}
152 		switch(trigger){
153 		case 0:
154 			v |= mpbus[p[4]]->trigger;
155 			break;
156 		case PcmpHIGH:
157 			v |= TMedge;
158 			break;
159 		case PcmpLOW:
160 			v |= TMlevel;
161 			break;
162 		}
163 		break;
164 	case PcmpNMI:
165 		v |= TMedge|IPhigh|MTnmi;
166 		break;
167 	case PcmpSMI:
168 		v |= TMedge|IPhigh|MTsmi;
169 		break;
170 	case PcmpExtINT:
171 		v |= TMedge|IPhigh|MTei;
172 		break;
173 	}
174 
175 	return v;
176 }
177 
178 static void
mpparse(PCMP * pcmp)179 mpparse(PCMP* pcmp)
180 {
181 	u32int lo;
182 	u8int *e, *p;
183 	int i, n, bustype;
184 
185 	p = pcmp->entries;
186 	e = ((uchar*)pcmp)+l16get(pcmp->length);
187 	while(p < e) switch(*p){
188 	default:
189 		print("mpparse: unknown PCMP type %d (e-p %#ld)\n", *p, e-p);
190 		for(i = 0; p < e; i++){
191 			if(i && ((i & 0x0f) == 0))
192 				print("\n");
193 			print(" %#2.2ux", *p);
194 			p++;
195 		}
196 		print("\n");
197 		break;
198 	case PcmpPROCESSOR:
199 		/*
200 		 * Initialise the APIC if it is enabled (p[3] & 0x01).
201 		 * p[1] is the APIC ID, the memory mapped address comes
202 		 * from the PCMP structure as the addess is local to the
203 		 * CPU and identical for all. Indicate whether this is
204 		 * the bootstrap processor (p[3] & 0x02).
205 		 */
206 		DBG("mpparse: APIC %d pa %#ux useable %d\n",
207 			p[1], l32get(pcmp->apicpa), p[3] & 0x01);
208 		if(p[3] & 0x01)
209 			apicinit(p[1], l32get(pcmp->apicpa), p[3] & 0x02);
210 		p += 20;
211 		break;
212 	case PcmpBUS:
213 		DBG("mpparse: bus: %d type %6.6s\n", p[1], (char*)p+2);
214 		if(mpbus[p[1]] != nil){
215 			print("mpparse: bus %d already allocated\n", p[1]);
216 			p += 8;
217 			break;
218 		}
219 		for(i = 0; i < nelem(mpbusdef); i++){
220 			if(memcmp(p+2, mpbusdef[i].type, 6) != 0)
221 				continue;
222 			if(memcmp(p+2, "ISA   ", 6) == 0){
223 				if(mpisabusno != -1){
224 					print("mpparse: bus %d already have ISA bus %d\n",
225 						p[1], mpisabusno);
226 					continue;
227 				}
228 				mpisabusno = p[1];
229 			}
230 			mpbus[p[1]] = &mpbusdef[i];
231 			break;
232 		}
233 		if(mpbus[p[1]] == nil)
234 			print("mpparse: bus %d type %6.6s unknown\n",
235 				p[1], (char*)p+2);
236 
237 		p += 8;
238 		break;
239 	case PcmpIOAPIC:
240 		/*
241 		 * Initialise the IOAPIC if it is enabled (p[3] & 0x01).
242 		 * p[1] is the APIC ID, p[4-7] is the memory mapped address.
243 		 */
244 		DBG("mpparse: IOAPIC %d pa %#ux useable %d\n",
245 			p[1], l32get(p+4), p[3] & 0x01);
246 		if(p[3] & 0x01)
247 			ioapicinit(p[1], l32get(p+4));
248 
249 		p += 8;
250 		break;
251 	case PcmpIOINTR:
252 		/*
253 		 * p[1] is the interrupt type;
254 		 * p[2-3] contains the polarity and trigger mode;
255 		 * p[4] is the source bus;
256 		 * p[5] is the IRQ on the source bus;
257 		 * p[6] is the destination APIC;
258 		 * p[7] is the INITIN pin on the destination APIC.
259 		 */
260 		if(p[6] == 0xff){
261 			mpintrprint("routed to all IOAPICs", p);
262 			p += 8;
263 			break;
264 		}
265 		if((lo = mpmkintr(p)) == 0){
266 			p += 8;
267 			break;
268 		}
269 		if(DBGFLG)
270 			mpintrprint(nil, p);
271 
272 		/*
273 		 * Always present the device number in the style
274 		 * of a PCI Interrupt Assignment Entry. For the ISA
275 		 * bus the IRQ is the device number but unencoded.
276 		 * May need to handle other buses here in the future
277 		 * (but unlikely).
278 		 */
279 		bustype = -1;
280 		if(memcmp(mpbus[p[4]]->type, "PCI   ", 6) == 0)
281 			bustype = BusPCI;
282 		else if(memcmp(mpbus[p[4]]->type, "ISA   ", 6) == 0)
283 			bustype = BusISA;
284 		if(bustype != -1)
285 			ioapicintrinit(bustype, p[4], p[6], p[7], p[5], lo);
286 
287 		p += 8;
288 		break;
289 	case PcmpLINTR:
290 		/*
291 		 * Format is the same as IOINTR above.
292 		 */
293 		if((lo = mpmkintr(p)) == 0){
294 			p += 8;
295 			break;
296 		}
297 		if(DBGFLG)
298 			mpintrprint(nil, p);
299 
300 		/*
301 		 * Everything was checked in mpmkintr above.
302 		 */
303 		if(p[6] == 0xff){
304 			for(i = 0; i < Napic; i++){
305 				if(!ioapic[i].useable || ioapic[i].addr != nil)
306 					continue;
307 				ioapic[i].lvt[p[7]] = lo;
308 			}
309 		}
310 		else
311 			ioapic[p[6]].lvt[p[7]] = lo;
312 		p += 8;
313 		break;
314 	}
315 
316 	/*
317 	 * There's nothing of real interest in the extended table,
318 	 * should just move along, but check it for consistency.
319 	 */
320 	p = e;
321 	e = p + l16get(pcmp->xlength);
322 	while(p < e) switch(*p){
323 	default:
324 		n = p[1];
325 		print("mpparse: unknown extended entry %d length %d\n", *p, n);
326 		for(i = 0; i < n; i++){
327 			if(i && ((i & 0x0f) == 0))
328 				print("\n");
329 			print(" %#2.2ux", *p);
330 			p++;
331 		}
332 		print("\n");
333 		break;
334 	case PcmpSASM:
335 		DBG("address space mapping\n");
336 		DBG(" bus %d type %d base %#llux length %#llux\n",
337 			p[2], p[3], l64get(p+4), l64get(p+12));
338 		p += p[1];
339 		break;
340 	case PcmpHIERARCHY:
341 		DBG("bus hierarchy descriptor\n");
342 		DBG(" bus %d sd %d parent bus %d\n",
343 			p[2], p[3], p[4]);
344 		p += p[1];
345 		break;
346 	case PcmpCBASM:
347 		DBG("compatibility bus address space modifier\n");
348 		DBG(" bus %d pr %d range list %d\n",
349 			p[2], p[3], l32get(p+4));
350 		p += p[1];
351 		break;
352 	}
353 }
354 
355 static int
sigchecksum(void * address,int length)356 sigchecksum(void* address, int length)
357 {
358 	u8int *p, sum;
359 
360 	sum = 0;
361 	for(p = address; length-- > 0; p++)
362 		sum += *p;
363 
364 	return sum;
365 }
366 
367 static void*
sigscan(u8int * address,int length,char * signature)368 sigscan(u8int* address, int length, char* signature)
369 {
370 	u8int *e, *p;
371 	int siglength;
372 
373 	DBG("check for %s in system base memory @ %#p\n", signature, address);
374 
375 	e = address+length;
376 	siglength = strlen(signature);
377 	for(p = address; p+siglength < e; p += 16){
378 		if(memcmp(p, signature, siglength))
379 			continue;
380 		return p;
381 	}
382 
383 	return nil;
384 }
385 
386 static void*
sigsearch(char * signature)387 sigsearch(char* signature)
388 {
389 	uintptr p;
390 	u8int *bda;
391 	void *r;
392 
393 	/*
394 	 * Search for the data structure:
395 	 * 1) within the first KiB of the Extended BIOS Data Area (EBDA), or
396 	 * 2) within the last KiB of system base memory if the EBDA segment
397 	 *    is undefined, or
398 	 * 3) within the BIOS ROM address space between 0xf0000 and 0xfffff
399 	 *    (but will actually check 0xe0000 to 0xfffff).
400 	 */
401 	bda = BIOSSEG(0x40);
402 	if(memcmp(KADDR(0xfffd9), "EISA", 4) == 0){
403 		if((p = (bda[0x0f]<<8)|bda[0x0e]) != 0){
404 			if((r = sigscan(BIOSSEG(p), 1024, signature)) != nil)
405 				return r;
406 		}
407 	}
408 
409 	if((p = ((bda[0x14]<<8)|bda[0x13])*1024) != 0){
410 		if((r = sigscan(KADDR(p-1024), 1024, signature)) != nil)
411 			return r;
412 	}
413 	if((r = sigscan(KADDR(0xa0000-1024), 1024, signature)) != nil)
414 		return r;
415 
416 	return sigscan(BIOSSEG(0xe000), 0x20000, signature);
417 }
418 
419 void
mpsinit(void)420 mpsinit(void)
421 {
422 	u8int *p;
423 	int i, n;
424 	_MP_ *mp;
425 	PCMP *pcmp;
426 
427 	if((mp = sigsearch("_MP_")) == nil)
428 		return;
429 	if(DBGFLG){
430 		DBG("_MP_ @ %#p, addr %#ux length %ud rev %d",
431 			mp, l32get(mp->addr), mp->length, mp->revision);
432 		for(i = 0; i < sizeof(mp->feature); i++)
433 			DBG(" %2.2#ux", mp->feature[i]);
434 		DBG("\n");
435 	}
436 	if(mp->revision != 1 && mp->revision != 4)
437 		return;
438 	if(sigchecksum(mp, mp->length*16) != 0)
439 		return;
440 
441 	if((pcmp = vmap(l32get(mp->addr), sizeof(PCMP))) == nil)
442 		return;
443 	if(pcmp->revision != 1 && pcmp->revision != 4){
444 		vunmap(pcmp, sizeof(PCMP));
445 		return;
446 	}
447 	n = l16get(pcmp->length) + l16get(pcmp->xlength);
448 	vunmap(pcmp, sizeof(PCMP));
449 	if((pcmp = vmap(l32get(mp->addr), n)) == nil)
450 		return;
451 	if(sigchecksum(pcmp, l16get(pcmp->length)) != 0){
452 		vunmap(pcmp, n);
453 		return;
454 	}
455 	if(DBGFLG){
456 		DBG("PCMP @ %#p length %#ux revision %d\n",
457 			pcmp, l16get(pcmp->length), pcmp->revision);
458 		DBG(" %20.20s oaddr %#ux olength %#ux\n",
459 			(char*)pcmp->string, l32get(pcmp->oaddr),
460 			l16get(pcmp->olength));
461 		DBG(" entry %d apicpa %#ux\n",
462 			l16get(pcmp->entry), l32get(pcmp->apicpa));
463 
464 		DBG(" xlength %#ux xchecksum %#ux\n",
465 			l16get(pcmp->xlength), pcmp->xchecksum);
466 	}
467 	if(pcmp->xchecksum != 0){
468 		p = ((u8int*)pcmp) + l16get(pcmp->length);
469 		i = sigchecksum(p, l16get(pcmp->xlength));
470 		if(((i+pcmp->xchecksum) & 0xff) != 0){
471 			print("extended table checksums to %#ux\n", i);
472 			vunmap(pcmp, n);
473 			return;
474 		}
475 	}
476 
477 	/*
478 	 * Parse the PCMP table and set up the datastructures
479 	 * for later interrupt enabling and application processor
480 	 * startup.
481 	 */
482 	mpparse(pcmp);
483 	mpacpi();
484 
485 	apicdump();
486 	ioapicdump();
487 }
488