xref: /titanic_44/usr/src/uts/intel/io/pci/pci_resource.c (revision feef89cf5f5fee792c1a396bb0e48070935cf65a)
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 2010 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_maxbus;
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_maxbus; 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->ml_address = orig->ml_address;
156 		new->ml_size = orig->ml_size;
157 		new->ml_prev = orig;
158 
159 		/* sorted insertion of 'new' into ranges list */
160 		for (current = ranges, prev = NULL; current != NULL;
161 		    prev = current, current = current->ml_next)
162 			if (new->ml_address < current->ml_address)
163 				break;
164 
165 		if (prev == NULL) {
166 			/* place at beginning of (possibly) empty list */
167 			new->ml_next = ranges;
168 			ranges = new;
169 		} else {
170 			/* place in list (possibly at end) */
171 			new->ml_next = current;
172 			prev->ml_next = new;
173 		}
174 	}
175 
176 	/* scan the list, perform trimming */
177 	current = ranges;
178 	while (current != NULL) {
179 		struct memlist *next = current->ml_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->ml_prev points to the original range)
188 		 */
189 		if ((current->ml_address + current->ml_size) > next->ml_address)
190 			current->ml_prev->ml_size =
191 			    next->ml_address - current->ml_address;
192 
193 		current = next;
194 	}
195 
196 	/* discard the list */
197 	memlist_free_all(&ranges);	/* OK if ranges == NULL */
198 }
199 
200 static int
201 acpi_find_bus_res(int bus, int type, struct memlist **res)
202 {
203 
204 	switch (type) {
205 	case IO_TYPE:
206 		*res = acpi_io_res[bus];
207 		break;
208 	case MEM_TYPE:
209 		*res = acpi_mem_res[bus];
210 		break;
211 	case PREFETCH_TYPE:
212 		*res = acpi_pmem_res[bus];
213 		break;
214 	case BUSRANGE_TYPE:
215 		*res = acpi_bus_res[bus];
216 		break;
217 	default:
218 		*res = NULL;
219 		break;
220 	}
221 
222 	/* memlist_count() treats NULL head as zero-length */
223 	return (memlist_count(*res));
224 }
225 
226 void
227 bus_res_fini(void)
228 {
229 	int bus;
230 
231 	for (bus = 0; bus <= pci_bios_maxbus; bus++) {
232 		memlist_free_all(&acpi_io_res[bus]);
233 		memlist_free_all(&acpi_mem_res[bus]);
234 		memlist_free_all(&acpi_pmem_res[bus]);
235 		memlist_free_all(&acpi_bus_res[bus]);
236 	}
237 }
238 
239 
240 struct memlist **
241 rlistpp(UINT8 t, UINT8 flags, int bus)
242 {
243 	switch (t) {
244 
245 		case ACPI_MEMORY_RANGE:
246 			/* is this really the best we've got? */
247 			if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY)
248 				return (&acpi_pmem_res[bus]);
249 			else
250 				return (&acpi_mem_res[bus]);
251 
252 		case ACPI_IO_RANGE:	return &acpi_io_res[bus];
253 		case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus];
254 	}
255 	return ((struct memlist **)NULL);
256 }
257 
258 
259 ACPI_STATUS
260 acpi_wr_cb(ACPI_RESOURCE *rp, void *context)
261 {
262 	int bus = (intptr_t)context;
263 
264 	/* ignore consumed resources */
265 	if (rp->Data.Address.ProducerConsumer == 1)
266 		return (AE_OK);
267 
268 	switch (rp->Type) {
269 	case ACPI_RESOURCE_TYPE_IRQ:
270 		/* never expect to see a PCI bus produce an Interrupt */
271 		dprintf("%s\n", "IRQ");
272 		break;
273 
274 	case ACPI_RESOURCE_TYPE_DMA:
275 		/* never expect to see a PCI bus produce DMA */
276 		dprintf("%s\n", "DMA");
277 		break;
278 
279 	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
280 		dprintf("%s\n", "START_DEPENDENT");
281 		break;
282 
283 	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
284 		dprintf("%s\n", "END_DEPENDENT");
285 		break;
286 
287 	case ACPI_RESOURCE_TYPE_IO:
288 		if (rp->Data.Io.AddressLength == 0)
289 			break;
290 		acpi_cb_cnt++;
291 		memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum,
292 		    rp->Data.Io.AddressLength);
293 		break;
294 
295 	case ACPI_RESOURCE_TYPE_FIXED_IO:
296 		/* only expect to see this as a consumer */
297 		dprintf("%s\n", "FIXED_IO");
298 		break;
299 
300 	case ACPI_RESOURCE_TYPE_VENDOR:
301 		dprintf("%s\n", "VENDOR");
302 		break;
303 
304 	case ACPI_RESOURCE_TYPE_END_TAG:
305 		dprintf("%s\n", "END_TAG");
306 		break;
307 
308 	case ACPI_RESOURCE_TYPE_MEMORY24:
309 		/* only expect to see this as a consumer */
310 		dprintf("%s\n", "MEMORY24");
311 		break;
312 
313 	case ACPI_RESOURCE_TYPE_MEMORY32:
314 		/* only expect to see this as a consumer */
315 		dprintf("%s\n", "MEMORY32");
316 		break;
317 
318 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
319 		/* only expect to see this as a consumer */
320 		dprintf("%s\n", "FIXED_MEMORY32");
321 		break;
322 
323 	case ACPI_RESOURCE_TYPE_ADDRESS16:
324 		if (rp->Data.Address16.AddressLength == 0)
325 			break;
326 		acpi_cb_cnt++;
327 		memlist_insert(rlistpp(rp->Data.Address16.ResourceType,
328 		    rp->Data.Address16.Info.TypeSpecific, bus),
329 		    rp->Data.Address16.Minimum,
330 		    rp->Data.Address16.AddressLength);
331 		break;
332 
333 	case ACPI_RESOURCE_TYPE_ADDRESS32:
334 		if (rp->Data.Address32.AddressLength == 0)
335 			break;
336 		acpi_cb_cnt++;
337 		memlist_insert(rlistpp(rp->Data.Address32.ResourceType,
338 		    rp->Data.Address32.Info.TypeSpecific, bus),
339 		    rp->Data.Address32.Minimum,
340 		    rp->Data.Address32.AddressLength);
341 		break;
342 
343 	case ACPI_RESOURCE_TYPE_ADDRESS64:
344 	/*
345 	 * We comment out this block because we currently cannot deal with
346 	 * PCI 64-bit addresses. Will revisit this when we add PCI 64-bit MMIO
347 	 * support.
348 	 */
349 #if 0
350 		if (rp->Data.Address64.AddressLength == 0)
351 			break;
352 		acpi_cb_cnt++;
353 		memlist_insert(rlistpp(rp->Data.Address64.ResourceType,
354 		    rp->Data.Address64.Info.TypeSpecific, bus),
355 		    rp->Data.Address64.Minimum,
356 		    rp->Data.Address64.AddressLength);
357 #endif
358 		break;
359 
360 	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
361 #if 0	/* Will revisit this when we add PCI 64-bit MMIO support */
362 		if (rp->Data.ExtAddress64.AddressLength == 0)
363 			break;
364 		acpi_cb_cnt++;
365 		memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType,
366 		    rp->Data.ExtAddress64.Info.TypeSpecific, bus),
367 		    rp->Data.ExtAddress64.Minimum,
368 		    rp->Data.ExtAddress64.AddressLength);
369 #endif
370 		break;
371 
372 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
373 		/* never expect to see a PCI bus produce an Interrupt */
374 		dprintf("%s\n", "EXTENDED_IRQ");
375 		break;
376 
377 	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
378 		/* never expect to see a PCI bus produce an GAS */
379 		dprintf("%s\n", "GENERIC_REGISTER");
380 		break;
381 	}
382 
383 	return (AE_OK);
384 }
385 
386 static void
387 mps_probe()
388 {
389 	uchar_t *extp;
390 	struct mps_fps_hdr *fpp = NULL;
391 	struct mps_ct_hdr *ctp;
392 	uintptr_t ebda_start, base_end;
393 	ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg;
394 
395 	base_size = *((ushort_t *)(0x413));
396 	ebda_seg = *((ushort_t *)(0x40e));
397 	ebda_start = ((uint32_t)ebda_seg) << 4;
398 	if (ebda_seg != 0) {
399 		fpp = (struct mps_fps_hdr *)find_sig(
400 		    (uchar_t *)ebda_start, 1024, "_MP_");
401 	}
402 	if (fpp == NULL) {
403 		base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0;
404 		if (base_end_seg != ebda_seg) {
405 			base_end = ((uintptr_t)base_end_seg) << 4;
406 			fpp = (struct mps_fps_hdr *)find_sig(
407 			    (uchar_t *)base_end, 1024, "_MP_");
408 		}
409 	}
410 	if (fpp == NULL) {
411 		fpp = (struct mps_fps_hdr *)find_sig(
412 		    (uchar_t *)0xF0000, 0x10000, "_MP_");
413 	}
414 
415 	if (fpp == NULL) {
416 		dprintf("MP Spec table doesn't exist");
417 		return;
418 	} else {
419 		dprintf("Found MP Floating Pointer Structure at %p\n",
420 		    (void *)fpp);
421 	}
422 
423 	if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) {
424 		dprintf("MP Floating Pointer Structure checksum error");
425 		return;
426 	}
427 
428 	ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr;
429 	if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */
430 		dprintf("MP Configuration Table signature is wrong");
431 		return;
432 	}
433 
434 	base_len = ctp->ct_len;
435 	if (checksum((uchar_t *)ctp, base_len) != 0) {
436 		dprintf("MP Configuration Table checksum error");
437 		return;
438 	}
439 	if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */
440 		dprintf("MP Spec 1.1 found - extended table doesn't exist");
441 		return;
442 	}
443 	if ((ext_len = ctp->ct_ext_tbl_len) == 0) {
444 		dprintf("MP Spec 1.4 found - extended table doesn't exist");
445 		return;
446 	}
447 	extp = (uchar_t *)ctp + base_len;
448 	if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) {
449 		dprintf("MP Extended Table checksum error");
450 		return;
451 	}
452 	mps_extp = extp;
453 	mps_ext_endp = mps_extp + ext_len;
454 }
455 
456 
457 static int
458 mps_find_bus_res(int bus, int type, struct memlist **res)
459 {
460 	struct sasm *sasmp;
461 	uchar_t *extp;
462 	int res_cnt;
463 
464 	if (mps_extp == NULL)
465 		return (0);
466 	extp = mps_extp;
467 	res_cnt = 0;
468 	while (extp < mps_ext_endp) {
469 		switch (*extp) {
470 		case SYS_AS_MAPPING:
471 			sasmp = (struct sasm *)extp;
472 			if (((int)sasmp->sasm_as_type) == type &&
473 			    ((int)sasmp->sasm_bus_id) == bus) {
474 				if (sasmp->sasm_as_base_hi != 0 ||
475 				    sasmp->sasm_as_len_hi != 0) {
476 					printf("64 bits address space\n");
477 					extp += SYS_AS_MAPPING_SIZE;
478 					break;
479 				}
480 				memlist_insert(res,
481 				    (uint64_t)sasmp->sasm_as_base,
482 				    sasmp->sasm_as_len);
483 				res_cnt++;
484 			}
485 			extp += SYS_AS_MAPPING_SIZE;
486 			break;
487 		case BUS_HIERARCHY_DESC:
488 			extp += BUS_HIERARCHY_DESC_SIZE;
489 			break;
490 		case COMP_BUS_AS_MODIFIER:
491 			extp += COMP_BUS_AS_MODIFIER_SIZE;
492 			break;
493 		default:
494 			cmn_err(CE_WARN, "Unknown descriptor type %d"
495 			    " in BIOS Multiprocessor Spec table.",
496 			    *extp);
497 			while (*res) {
498 				struct memlist *tmp = *res;
499 				*res = tmp->ml_next;
500 				memlist_free(tmp);
501 			}
502 			return (0);
503 		}
504 	}
505 	return (res_cnt);
506 }
507 
508 static void
509 hrt_probe()
510 {
511 	struct hrt_hdr *hrtp;
512 
513 	dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n");
514 	if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000,
515 	    0x10000, "$HRT")) == NULL) {
516 		dprintf("NO PCI Hot-Plug Resource Table");
517 		return;
518 	}
519 	dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp);
520 	if (hrtp->hrt_ver != 1) {
521 		dprintf("PCI Hot-Plug Resource Table version no. <> 1\n");
522 		return;
523 	}
524 	hrt_entry_cnt = (int)hrtp->hrt_entry_cnt;
525 	dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt);
526 	hrt_hpep = (struct php_entry *)(hrtp + 1);
527 }
528 
529 static int
530 hrt_find_bus_res(int bus, int type, struct memlist **res)
531 {
532 	int res_cnt, i;
533 	struct php_entry *hpep;
534 
535 	if (hrt_hpep == NULL || hrt_entry_cnt == 0)
536 		return (0);
537 	hpep = hrt_hpep;
538 	res_cnt = 0;
539 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
540 		if (hpep->php_pri_bus != bus)
541 			continue;
542 		if (type == IO_TYPE) {
543 			if (hpep->php_io_start == 0 || hpep->php_io_size == 0)
544 				continue;
545 			memlist_insert(res, (uint64_t)hpep->php_io_start,
546 			    (uint64_t)hpep->php_io_size);
547 			res_cnt++;
548 		} else if (type == MEM_TYPE) {
549 			if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0)
550 				continue;
551 			memlist_insert(res,
552 			    (uint64_t)(((int)hpep->php_mem_start) << 16),
553 			    (uint64_t)(((int)hpep->php_mem_size) << 16));
554 			res_cnt++;
555 		} else if (type == PREFETCH_TYPE) {
556 			if (hpep->php_pfmem_start == 0 ||
557 			    hpep->php_pfmem_size == 0)
558 				continue;
559 			memlist_insert(res,
560 			    (uint64_t)(((int)hpep->php_pfmem_start) << 16),
561 			    (uint64_t)(((int)hpep->php_pfmem_size) << 16));
562 			res_cnt++;
563 		}
564 	}
565 	return (res_cnt);
566 }
567 
568 static uchar_t *
569 find_sig(uchar_t *cp, int len, char *sig)
570 {
571 	long i;
572 
573 	/* Search for the "_MP_"  or "$HRT" signature */
574 	for (i = 0; i < len; i += 16) {
575 		if (cp[0] == sig[0] && cp[1] == sig[1] &&
576 		    cp[2] == sig[2] && cp[3] == sig[3])
577 			return (cp);
578 		cp += 16;
579 	}
580 	return (NULL);
581 }
582 
583 static int
584 checksum(unsigned char *cp, int len)
585 {
586 	int i;
587 	unsigned int cksum;
588 
589 	for (i = cksum = 0; i < len; i++)
590 		cksum += (unsigned int) *cp++;
591 
592 	return ((int)(cksum & 0xFF));
593 }
594 
595 #ifdef UNUSED_BUS_HIERARY_INFO
596 
597 /*
598  * At this point, the bus hierarchy entries do not appear to
599  * provide anything we can't find out from PCI config space.
600  * The only interesting bit is the ISA bus number, which we
601  * don't care.
602  */
603 int
604 mps_find_parent_bus(int bus)
605 {
606 	struct sasm *sasmp;
607 	uchar_t *extp;
608 
609 	if (mps_extp == NULL)
610 		return (-1);
611 
612 	extp = mps_extp;
613 	while (extp < mps_ext_endp) {
614 		bhdp = (struct bhd *)extp;
615 		switch (*extp) {
616 		case SYS_AS_MAPPING:
617 			extp += SYS_AS_MAPPING_SIZE;
618 			break;
619 		case BUS_HIERARCHY_DESC:
620 			if (bhdp->bhd_bus_id == bus)
621 				return (bhdp->bhd_parent);
622 			extp += BUS_HIERARCHY_DESC_SIZE;
623 			break;
624 		case COMP_BUS_AS_MODIFIER:
625 			extp += COMP_BUS_AS_MODIFIER_SIZE;
626 			break;
627 		default:
628 			cmn_err(CE_WARN, "Unknown descriptor type %d"
629 			    " in BIOS Multiprocessor Spec table.",
630 			    *extp);
631 			return (-1);
632 		}
633 	}
634 	return (-1);
635 }
636 
637 int
638 hrt_find_bus_range(int bus)
639 {
640 	int i, max_bus, sub_bus;
641 	struct php_entry *hpep;
642 
643 	if (hrt_hpep == NULL || hrt_entry_cnt == 0) {
644 		return (-1);
645 	}
646 	hpep = hrt_hpep;
647 	max_bus = -1;
648 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
649 		if (hpep->php_pri_bus != bus)
650 			continue;
651 		sub_bus = (int)hpep->php_subord_bus;
652 		if (sub_bus > max_bus)
653 			max_bus = sub_bus;
654 	}
655 	return (max_bus);
656 }
657 
658 #endif /* UNUSED_BUS_HIERARY_INFO */
659