xref: /titanic_41/usr/src/uts/intel/io/pci/pci_resource.c (revision 6e1fa242609208de48dfe1939b8814d4dff455a5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * pci_resource.c -- routines to retrieve available bus resources from
26  *		 the MP Spec. Table and Hotplug Resource Table
27  */
28 
29 #include <sys/types.h>
30 #include <sys/memlist.h>
31 #include <sys/pci_impl.h>
32 #include <sys/systm.h>
33 #include <sys/cmn_err.h>
34 #include <sys/acpi/acpi.h>
35 #include <sys/acpica.h>
36 #include "mps_table.h"
37 #include "pcihrt.h"
38 
39 extern int pci_boot_debug;
40 extern int pci_bios_nbus;
41 #define	dprintf	if (pci_boot_debug) printf
42 
43 static int tbl_init = 0;
44 static uchar_t *mps_extp = NULL;
45 static uchar_t *mps_ext_endp = NULL;
46 static struct php_entry *hrt_hpep;
47 static int hrt_entry_cnt = 0;
48 static int acpi_cb_cnt = 0;
49 
50 static void mps_probe(void);
51 static void acpi_pci_probe(void);
52 static int mps_find_bus_res(int, int, struct memlist **);
53 static void hrt_probe(void);
54 static int hrt_find_bus_res(int, int, struct memlist **);
55 static int acpi_find_bus_res(int, int, struct memlist **);
56 static uchar_t *find_sig(uchar_t *cp, int len, char *sig);
57 static int checksum(unsigned char *cp, int len);
58 static ACPI_STATUS acpi_wr_cb(ACPI_RESOURCE *rp, void *context);
59 void bus_res_fini(void);
60 static void acpi_trim_bus_ranges(void);
61 
62 struct memlist *acpi_io_res[256];
63 struct memlist *acpi_mem_res[256];
64 struct memlist *acpi_pmem_res[256];
65 struct memlist *acpi_bus_res[256];
66 
67 /*
68  * -1 = attempt ACPI resource discovery
69  *  0 = don't attempt ACPI resource discovery
70  *  1 = ACPI resource discovery successful
71  */
72 volatile int acpi_resource_discovery = -1;
73 
74 struct memlist *
75 find_bus_res(int bus, int type)
76 {
77 	struct memlist *res = NULL;
78 
79 	if (tbl_init == 0) {
80 		tbl_init = 1;
81 		acpi_pci_probe();
82 		hrt_probe();
83 		mps_probe();
84 	}
85 
86 	if (acpi_find_bus_res(bus, type, &res) > 0)
87 		return (res);
88 
89 	if (hrt_find_bus_res(bus, type, &res) > 0)
90 		return (res);
91 
92 	(void) mps_find_bus_res(bus, type, &res);
93 	return (res);
94 }
95 
96 
97 static void
98 acpi_pci_probe(void)
99 {
100 	ACPI_HANDLE ah;
101 	dev_info_t *dip;
102 	int bus;
103 
104 	if (acpi_resource_discovery == 0)
105 		return;
106 
107 	for (bus = 0; bus < pci_bios_nbus; bus++) {
108 		/* if no dip or no ACPI handle, no resources to discover */
109 		dip = pci_bus_res[bus].dip;
110 		if ((dip == NULL) ||
111 		    (ACPI_FAILURE(acpica_get_handle(dip, &ah))))
112 			continue;
113 
114 		(void) AcpiWalkResources(ah, "_CRS", acpi_wr_cb,
115 		    (void *)(uintptr_t)bus);
116 	}
117 
118 	if (acpi_cb_cnt > 0) {
119 		acpi_resource_discovery = 1;
120 		acpi_trim_bus_ranges();
121 	}
122 }
123 
124 /*
125  * Trim overlapping bus ranges in acpi_bus_res[]
126  * Some BIOSes report root-bridges with bus ranges that
127  * overlap, for example:"0..255" and "8..255". Lower-numbered
128  * ranges are trimmed by upper-numbered ranges (so "0..255" would
129  * be trimmed to "0..7", in the example).
130  */
131 static void
132 acpi_trim_bus_ranges()
133 {
134 	struct memlist *ranges, *current;
135 	int bus;
136 
137 	ranges = NULL;
138 
139 	/*
140 	 * Assumptions:
141 	 *  - there exists at most 1 bus range entry for each bus number
142 	 *  - there are no (broken) ranges that start at the same bus number
143 	 */
144 	for (bus = 0; bus < 256; bus++) {
145 		struct memlist *prev, *orig, *new;
146 		/* skip buses with no range entry */
147 		if ((orig = acpi_bus_res[bus]) == NULL)
148 			continue;
149 
150 		/*
151 		 * create copy of existing range and overload
152 		 * 'prev' pointer to link existing to new copy
153 		 */
154 		new = memlist_alloc();
155 		new->address = orig->address;
156 		new->size = orig->size;
157 		new->prev = orig;
158 
159 		/* sorted insertion of 'new' into ranges list */
160 		for (current = ranges, prev = NULL; current != NULL;
161 		    prev = current, current = current->next)
162 			if (new->address < current->address)
163 				break;
164 
165 		if (prev == NULL) {
166 			/* place at beginning of (possibly) empty list */
167 			new->next = ranges;
168 			ranges = new;
169 		} else {
170 			/* place in list (possibly at end) */
171 			new->next = current;
172 			prev->next = new;
173 		}
174 	}
175 
176 	/* scan the list, perform trimming */
177 	current = ranges;
178 	while (current != NULL) {
179 		struct memlist *next = current->next;
180 
181 		/* done when no range above current */
182 		if (next == NULL)
183 			break;
184 
185 		/*
186 		 * trim size in original range element
187 		 * (current->prev points to the original range)
188 		 */
189 		if ((current->address + current->size) > next->address)
190 			current->prev->size = next->address - current->address;
191 
192 		current = next;
193 	}
194 
195 	/* discard the list */
196 	memlist_free_all(&ranges);	/* OK if ranges == NULL */
197 }
198 
199 static int
200 acpi_find_bus_res(int bus, int type, struct memlist **res)
201 {
202 
203 	switch (type) {
204 	case IO_TYPE:
205 		*res = acpi_io_res[bus];
206 		break;
207 	case MEM_TYPE:
208 		*res = acpi_mem_res[bus];
209 		break;
210 	case PREFETCH_TYPE:
211 		*res = acpi_pmem_res[bus];
212 		break;
213 	case BUSRANGE_TYPE:
214 		*res = acpi_bus_res[bus];
215 		break;
216 	default:
217 		*res = NULL;
218 		break;
219 	}
220 
221 	/* memlist_count() treats NULL head as zero-length */
222 	return (memlist_count(*res));
223 }
224 
225 void
226 bus_res_fini(void)
227 {
228 	int bus;
229 
230 	for (bus = 0; bus < pci_bios_nbus; bus++) {
231 		memlist_free_all(&acpi_io_res[bus]);
232 		memlist_free_all(&acpi_mem_res[bus]);
233 		memlist_free_all(&acpi_pmem_res[bus]);
234 		memlist_free_all(&acpi_bus_res[bus]);
235 	}
236 }
237 
238 
239 struct memlist **
240 rlistpp(UINT8 t, UINT8 flags, int bus)
241 {
242 	switch (t) {
243 
244 		case ACPI_MEMORY_RANGE:
245 			/* is this really the best we've got? */
246 			if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY)
247 				return (&acpi_pmem_res[bus]);
248 			else
249 				return (&acpi_mem_res[bus]);
250 
251 		case ACPI_IO_RANGE:	return &acpi_io_res[bus];
252 		case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus];
253 	}
254 	return ((struct memlist **)NULL);
255 }
256 
257 
258 ACPI_STATUS
259 acpi_wr_cb(ACPI_RESOURCE *rp, void *context)
260 {
261 	int bus = (intptr_t)context;
262 
263 	/* ignore consumed resources */
264 	if (rp->Data.Address.ProducerConsumer == 1)
265 		return (AE_OK);
266 
267 	switch (rp->Type) {
268 	case ACPI_RESOURCE_TYPE_IRQ:
269 		/* never expect to see a PCI bus produce an Interrupt */
270 		dprintf("%s\n", "IRQ");
271 		break;
272 
273 	case ACPI_RESOURCE_TYPE_DMA:
274 		/* never expect to see a PCI bus produce DMA */
275 		dprintf("%s\n", "DMA");
276 		break;
277 
278 	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
279 		dprintf("%s\n", "START_DEPENDENT");
280 		break;
281 
282 	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
283 		dprintf("%s\n", "END_DEPENDENT");
284 		break;
285 
286 	case ACPI_RESOURCE_TYPE_IO:
287 		if (rp->Data.Io.AddressLength == 0)
288 			break;
289 		acpi_cb_cnt++;
290 		memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum,
291 		    rp->Data.Io.AddressLength);
292 		break;
293 
294 	case ACPI_RESOURCE_TYPE_FIXED_IO:
295 		/* only expect to see this as a consumer */
296 		dprintf("%s\n", "FIXED_IO");
297 		break;
298 
299 	case ACPI_RESOURCE_TYPE_VENDOR:
300 		dprintf("%s\n", "VENDOR");
301 		break;
302 
303 	case ACPI_RESOURCE_TYPE_END_TAG:
304 		dprintf("%s\n", "END_TAG");
305 		break;
306 
307 	case ACPI_RESOURCE_TYPE_MEMORY24:
308 		/* only expect to see this as a consumer */
309 		dprintf("%s\n", "MEMORY24");
310 		break;
311 
312 	case ACPI_RESOURCE_TYPE_MEMORY32:
313 		/* only expect to see this as a consumer */
314 		dprintf("%s\n", "MEMORY32");
315 		break;
316 
317 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
318 		/* only expect to see this as a consumer */
319 		dprintf("%s\n", "FIXED_MEMORY32");
320 		break;
321 
322 	case ACPI_RESOURCE_TYPE_ADDRESS16:
323 		if (rp->Data.Address16.AddressLength == 0)
324 			break;
325 		acpi_cb_cnt++;
326 		memlist_insert(rlistpp(rp->Data.Address16.ResourceType,
327 		    rp->Data.Address16.Info.TypeSpecific, bus),
328 		    rp->Data.Address16.Minimum,
329 		    rp->Data.Address16.AddressLength);
330 		break;
331 
332 	case ACPI_RESOURCE_TYPE_ADDRESS32:
333 		if (rp->Data.Address32.AddressLength == 0)
334 			break;
335 		acpi_cb_cnt++;
336 		memlist_insert(rlistpp(rp->Data.Address32.ResourceType,
337 		    rp->Data.Address32.Info.TypeSpecific, bus),
338 		    rp->Data.Address32.Minimum,
339 		    rp->Data.Address32.AddressLength);
340 		break;
341 
342 	case ACPI_RESOURCE_TYPE_ADDRESS64:
343 		if (rp->Data.Address64.AddressLength == 0)
344 			break;
345 		acpi_cb_cnt++;
346 		memlist_insert(rlistpp(rp->Data.Address64.ResourceType,
347 		    rp->Data.Address64.Info.TypeSpecific, bus),
348 		    rp->Data.Address64.Minimum,
349 		    rp->Data.Address64.AddressLength);
350 		break;
351 
352 	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
353 		if (rp->Data.ExtAddress64.AddressLength == 0)
354 			break;
355 		acpi_cb_cnt++;
356 		memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType,
357 		    rp->Data.ExtAddress64.Info.TypeSpecific, bus),
358 		    rp->Data.ExtAddress64.Minimum,
359 		    rp->Data.ExtAddress64.AddressLength);
360 		break;
361 
362 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
363 		/* never expect to see a PCI bus produce an Interrupt */
364 		dprintf("%s\n", "EXTENDED_IRQ");
365 		break;
366 
367 	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
368 		/* never expect to see a PCI bus produce an GAS */
369 		dprintf("%s\n", "GENERIC_REGISTER");
370 		break;
371 	}
372 
373 	return (AE_OK);
374 }
375 
376 static void
377 mps_probe()
378 {
379 	uchar_t *extp;
380 	struct mps_fps_hdr *fpp = NULL;
381 	struct mps_ct_hdr *ctp;
382 	uintptr_t ebda_start, base_end;
383 	ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg;
384 
385 	base_size = *((ushort_t *)(0x413));
386 	ebda_seg = *((ushort_t *)(0x40e));
387 	ebda_start = ((uint32_t)ebda_seg) << 4;
388 	if (ebda_seg != 0) {
389 		fpp = (struct mps_fps_hdr *)find_sig(
390 		    (uchar_t *)ebda_start, 1024, "_MP_");
391 	}
392 	if (fpp == NULL) {
393 		base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0;
394 		if (base_end_seg != ebda_seg) {
395 			base_end = ((uintptr_t)base_end_seg) << 4;
396 			fpp = (struct mps_fps_hdr *)find_sig(
397 			    (uchar_t *)base_end, 1024, "_MP_");
398 		}
399 	}
400 	if (fpp == NULL) {
401 		fpp = (struct mps_fps_hdr *)find_sig(
402 		    (uchar_t *)0xF0000, 0x10000, "_MP_");
403 	}
404 
405 	if (fpp == NULL) {
406 		dprintf("MP Spec table doesn't exist");
407 		return;
408 	} else {
409 		dprintf("Found MP Floating Pointer Structure at %p\n",
410 		    (void *)fpp);
411 	}
412 
413 	if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) {
414 		dprintf("MP Floating Pointer Structure checksum error");
415 		return;
416 	}
417 
418 	ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr;
419 	if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */
420 		dprintf("MP Configuration Table signature is wrong");
421 		return;
422 	}
423 
424 	base_len = ctp->ct_len;
425 	if (checksum((uchar_t *)ctp, base_len) != 0) {
426 		dprintf("MP Configuration Table checksum error");
427 		return;
428 	}
429 	if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */
430 		dprintf("MP Spec 1.1 found - extended table doesn't exist");
431 		return;
432 	}
433 	if ((ext_len = ctp->ct_ext_tbl_len) == 0) {
434 		dprintf("MP Spec 1.4 found - extended table doesn't exist");
435 		return;
436 	}
437 	extp = (uchar_t *)ctp + base_len;
438 	if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) {
439 		dprintf("MP Extended Table checksum error");
440 		return;
441 	}
442 	mps_extp = extp;
443 	mps_ext_endp = mps_extp + ext_len;
444 }
445 
446 
447 static int
448 mps_find_bus_res(int bus, int type, struct memlist **res)
449 {
450 	struct sasm *sasmp;
451 	uchar_t *extp;
452 	int res_cnt;
453 
454 	if (mps_extp == NULL)
455 		return (0);
456 	extp = mps_extp;
457 	res_cnt = 0;
458 	while (extp < mps_ext_endp) {
459 		switch (*extp) {
460 		case SYS_AS_MAPPING:
461 			sasmp = (struct sasm *)extp;
462 			if (((int)sasmp->sasm_as_type) == type &&
463 			    ((int)sasmp->sasm_bus_id) == bus) {
464 				if (sasmp->sasm_as_base_hi != 0 ||
465 				    sasmp->sasm_as_len_hi != 0) {
466 					printf("64 bits address space\n");
467 					extp += SYS_AS_MAPPING_SIZE;
468 					break;
469 				}
470 				memlist_insert(res,
471 				    (uint64_t)sasmp->sasm_as_base,
472 				    sasmp->sasm_as_len);
473 				res_cnt++;
474 			}
475 			extp += SYS_AS_MAPPING_SIZE;
476 			break;
477 		case BUS_HIERARCHY_DESC:
478 			extp += BUS_HIERARCHY_DESC_SIZE;
479 			break;
480 		case COMP_BUS_AS_MODIFIER:
481 			extp += COMP_BUS_AS_MODIFIER_SIZE;
482 			break;
483 		default:
484 			cmn_err(CE_WARN, "Unknown descriptor type %d"
485 			    " in BIOS Multiprocessor Spec table.",
486 			    *extp);
487 			while (*res) {
488 				struct memlist *tmp = *res;
489 				*res = tmp->next;
490 				memlist_free(tmp);
491 			}
492 			return (0);
493 		}
494 	}
495 	return (res_cnt);
496 }
497 
498 static void
499 hrt_probe()
500 {
501 	struct hrt_hdr *hrtp;
502 
503 	dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n");
504 	if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000,
505 	    0x10000, "$HRT")) == NULL) {
506 		dprintf("NO PCI Hot-Plug Resource Table");
507 		return;
508 	}
509 	dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp);
510 	if (hrtp->hrt_ver != 1) {
511 		dprintf("PCI Hot-Plug Resource Table version no. <> 1\n");
512 		return;
513 	}
514 	hrt_entry_cnt = (int)hrtp->hrt_entry_cnt;
515 	dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt);
516 	hrt_hpep = (struct php_entry *)(hrtp + 1);
517 }
518 
519 static int
520 hrt_find_bus_res(int bus, int type, struct memlist **res)
521 {
522 	int res_cnt, i;
523 	struct php_entry *hpep;
524 
525 	if (hrt_hpep == NULL || hrt_entry_cnt == 0)
526 		return (0);
527 	hpep = hrt_hpep;
528 	res_cnt = 0;
529 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
530 		if (hpep->php_pri_bus != bus)
531 			continue;
532 		if (type == IO_TYPE) {
533 			if (hpep->php_io_start == 0 || hpep->php_io_size == 0)
534 				continue;
535 			memlist_insert(res, (uint64_t)hpep->php_io_start,
536 			    (uint64_t)hpep->php_io_size);
537 			res_cnt++;
538 		} else if (type == MEM_TYPE) {
539 			if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0)
540 				continue;
541 			memlist_insert(res,
542 			    (uint64_t)(((int)hpep->php_mem_start) << 16),
543 			    (uint64_t)(((int)hpep->php_mem_size) << 16));
544 			res_cnt++;
545 		} else if (type == PREFETCH_TYPE) {
546 			if (hpep->php_pfmem_start == 0 ||
547 			    hpep->php_pfmem_size == 0)
548 				continue;
549 			memlist_insert(res,
550 			    (uint64_t)(((int)hpep->php_pfmem_start) << 16),
551 			    (uint64_t)(((int)hpep->php_pfmem_size) << 16));
552 			res_cnt++;
553 		}
554 	}
555 	return (res_cnt);
556 }
557 
558 static uchar_t *
559 find_sig(uchar_t *cp, int len, char *sig)
560 {
561 	long i;
562 
563 	/* Search for the "_MP_"  or "$HRT" signature */
564 	for (i = 0; i < len; i += 16) {
565 		if (cp[0] == sig[0] && cp[1] == sig[1] &&
566 		    cp[2] == sig[2] && cp[3] == sig[3])
567 			return (cp);
568 		cp += 16;
569 	}
570 	return (NULL);
571 }
572 
573 static int
574 checksum(unsigned char *cp, int len)
575 {
576 	int i;
577 	unsigned int cksum;
578 
579 	for (i = cksum = 0; i < len; i++)
580 		cksum += (unsigned int) *cp++;
581 
582 	return ((int)(cksum & 0xFF));
583 }
584 
585 #ifdef UNUSED_BUS_HIERARY_INFO
586 
587 /*
588  * At this point, the bus hierarchy entries do not appear to
589  * provide anything we can't find out from PCI config space.
590  * The only interesting bit is the ISA bus number, which we
591  * don't care.
592  */
593 int
594 mps_find_parent_bus(int bus)
595 {
596 	struct sasm *sasmp;
597 	uchar_t *extp;
598 
599 	if (mps_extp == NULL)
600 		return (-1);
601 
602 	extp = mps_extp;
603 	while (extp < mps_ext_endp) {
604 		bhdp = (struct bhd *)extp;
605 		switch (*extp) {
606 		case SYS_AS_MAPPING:
607 			extp += SYS_AS_MAPPING_SIZE;
608 			break;
609 		case BUS_HIERARCHY_DESC:
610 			if (bhdp->bhd_bus_id == bus)
611 				return (bhdp->bhd_parent);
612 			extp += BUS_HIERARCHY_DESC_SIZE;
613 			break;
614 		case COMP_BUS_AS_MODIFIER:
615 			extp += COMP_BUS_AS_MODIFIER_SIZE;
616 			break;
617 		default:
618 			cmn_err(CE_WARN, "Unknown descriptor type %d"
619 			    " in BIOS Multiprocessor Spec table.",
620 			    *extp);
621 			return (-1);
622 		}
623 	}
624 	return (-1);
625 }
626 
627 int
628 hrt_find_bus_range(int bus)
629 {
630 	int i, max_bus, sub_bus;
631 	struct php_entry *hpep;
632 
633 	if (hrt_hpep == NULL || hrt_entry_cnt == 0) {
634 		return (-1);
635 	}
636 	hpep = hrt_hpep;
637 	max_bus = -1;
638 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
639 		if (hpep->php_pri_bus != bus)
640 			continue;
641 		sub_bus = (int)hpep->php_subord_bus;
642 		if (sub_bus > max_bus)
643 			max_bus = sub_bus;
644 	}
645 	return (max_bus);
646 }
647 
648 #endif /* UNUSED_BUS_HIERARY_INFO */
649