xref: /titanic_41/usr/src/uts/intel/io/pci/pci_resource.c (revision 8f588c8376a0fa02470fc4554ec83442b305020f)
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 	/*
344 	 * We comment out this block because we currently cannot deal with
345 	 * PCI 64-bit addresses. Will revisit this when we add PCI 64-bit MMIO
346 	 * support.
347 	 */
348 #if 0
349 		if (rp->Data.Address64.AddressLength == 0)
350 			break;
351 		acpi_cb_cnt++;
352 		memlist_insert(rlistpp(rp->Data.Address64.ResourceType,
353 		    rp->Data.Address64.Info.TypeSpecific, bus),
354 		    rp->Data.Address64.Minimum,
355 		    rp->Data.Address64.AddressLength);
356 #endif
357 		break;
358 
359 	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
360 #if 0	/* Will revisit this when we add PCI 64-bit MMIO support */
361 		if (rp->Data.ExtAddress64.AddressLength == 0)
362 			break;
363 		acpi_cb_cnt++;
364 		memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType,
365 		    rp->Data.ExtAddress64.Info.TypeSpecific, bus),
366 		    rp->Data.ExtAddress64.Minimum,
367 		    rp->Data.ExtAddress64.AddressLength);
368 #endif
369 		break;
370 
371 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
372 		/* never expect to see a PCI bus produce an Interrupt */
373 		dprintf("%s\n", "EXTENDED_IRQ");
374 		break;
375 
376 	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
377 		/* never expect to see a PCI bus produce an GAS */
378 		dprintf("%s\n", "GENERIC_REGISTER");
379 		break;
380 	}
381 
382 	return (AE_OK);
383 }
384 
385 static void
386 mps_probe()
387 {
388 	uchar_t *extp;
389 	struct mps_fps_hdr *fpp = NULL;
390 	struct mps_ct_hdr *ctp;
391 	uintptr_t ebda_start, base_end;
392 	ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg;
393 
394 	base_size = *((ushort_t *)(0x413));
395 	ebda_seg = *((ushort_t *)(0x40e));
396 	ebda_start = ((uint32_t)ebda_seg) << 4;
397 	if (ebda_seg != 0) {
398 		fpp = (struct mps_fps_hdr *)find_sig(
399 		    (uchar_t *)ebda_start, 1024, "_MP_");
400 	}
401 	if (fpp == NULL) {
402 		base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0;
403 		if (base_end_seg != ebda_seg) {
404 			base_end = ((uintptr_t)base_end_seg) << 4;
405 			fpp = (struct mps_fps_hdr *)find_sig(
406 			    (uchar_t *)base_end, 1024, "_MP_");
407 		}
408 	}
409 	if (fpp == NULL) {
410 		fpp = (struct mps_fps_hdr *)find_sig(
411 		    (uchar_t *)0xF0000, 0x10000, "_MP_");
412 	}
413 
414 	if (fpp == NULL) {
415 		dprintf("MP Spec table doesn't exist");
416 		return;
417 	} else {
418 		dprintf("Found MP Floating Pointer Structure at %p\n",
419 		    (void *)fpp);
420 	}
421 
422 	if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) {
423 		dprintf("MP Floating Pointer Structure checksum error");
424 		return;
425 	}
426 
427 	ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr;
428 	if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */
429 		dprintf("MP Configuration Table signature is wrong");
430 		return;
431 	}
432 
433 	base_len = ctp->ct_len;
434 	if (checksum((uchar_t *)ctp, base_len) != 0) {
435 		dprintf("MP Configuration Table checksum error");
436 		return;
437 	}
438 	if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */
439 		dprintf("MP Spec 1.1 found - extended table doesn't exist");
440 		return;
441 	}
442 	if ((ext_len = ctp->ct_ext_tbl_len) == 0) {
443 		dprintf("MP Spec 1.4 found - extended table doesn't exist");
444 		return;
445 	}
446 	extp = (uchar_t *)ctp + base_len;
447 	if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) {
448 		dprintf("MP Extended Table checksum error");
449 		return;
450 	}
451 	mps_extp = extp;
452 	mps_ext_endp = mps_extp + ext_len;
453 }
454 
455 
456 static int
457 mps_find_bus_res(int bus, int type, struct memlist **res)
458 {
459 	struct sasm *sasmp;
460 	uchar_t *extp;
461 	int res_cnt;
462 
463 	if (mps_extp == NULL)
464 		return (0);
465 	extp = mps_extp;
466 	res_cnt = 0;
467 	while (extp < mps_ext_endp) {
468 		switch (*extp) {
469 		case SYS_AS_MAPPING:
470 			sasmp = (struct sasm *)extp;
471 			if (((int)sasmp->sasm_as_type) == type &&
472 			    ((int)sasmp->sasm_bus_id) == bus) {
473 				if (sasmp->sasm_as_base_hi != 0 ||
474 				    sasmp->sasm_as_len_hi != 0) {
475 					printf("64 bits address space\n");
476 					extp += SYS_AS_MAPPING_SIZE;
477 					break;
478 				}
479 				memlist_insert(res,
480 				    (uint64_t)sasmp->sasm_as_base,
481 				    sasmp->sasm_as_len);
482 				res_cnt++;
483 			}
484 			extp += SYS_AS_MAPPING_SIZE;
485 			break;
486 		case BUS_HIERARCHY_DESC:
487 			extp += BUS_HIERARCHY_DESC_SIZE;
488 			break;
489 		case COMP_BUS_AS_MODIFIER:
490 			extp += COMP_BUS_AS_MODIFIER_SIZE;
491 			break;
492 		default:
493 			cmn_err(CE_WARN, "Unknown descriptor type %d"
494 			    " in BIOS Multiprocessor Spec table.",
495 			    *extp);
496 			while (*res) {
497 				struct memlist *tmp = *res;
498 				*res = tmp->next;
499 				memlist_free(tmp);
500 			}
501 			return (0);
502 		}
503 	}
504 	return (res_cnt);
505 }
506 
507 static void
508 hrt_probe()
509 {
510 	struct hrt_hdr *hrtp;
511 
512 	dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n");
513 	if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000,
514 	    0x10000, "$HRT")) == NULL) {
515 		dprintf("NO PCI Hot-Plug Resource Table");
516 		return;
517 	}
518 	dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp);
519 	if (hrtp->hrt_ver != 1) {
520 		dprintf("PCI Hot-Plug Resource Table version no. <> 1\n");
521 		return;
522 	}
523 	hrt_entry_cnt = (int)hrtp->hrt_entry_cnt;
524 	dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt);
525 	hrt_hpep = (struct php_entry *)(hrtp + 1);
526 }
527 
528 static int
529 hrt_find_bus_res(int bus, int type, struct memlist **res)
530 {
531 	int res_cnt, i;
532 	struct php_entry *hpep;
533 
534 	if (hrt_hpep == NULL || hrt_entry_cnt == 0)
535 		return (0);
536 	hpep = hrt_hpep;
537 	res_cnt = 0;
538 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
539 		if (hpep->php_pri_bus != bus)
540 			continue;
541 		if (type == IO_TYPE) {
542 			if (hpep->php_io_start == 0 || hpep->php_io_size == 0)
543 				continue;
544 			memlist_insert(res, (uint64_t)hpep->php_io_start,
545 			    (uint64_t)hpep->php_io_size);
546 			res_cnt++;
547 		} else if (type == MEM_TYPE) {
548 			if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0)
549 				continue;
550 			memlist_insert(res,
551 			    (uint64_t)(((int)hpep->php_mem_start) << 16),
552 			    (uint64_t)(((int)hpep->php_mem_size) << 16));
553 			res_cnt++;
554 		} else if (type == PREFETCH_TYPE) {
555 			if (hpep->php_pfmem_start == 0 ||
556 			    hpep->php_pfmem_size == 0)
557 				continue;
558 			memlist_insert(res,
559 			    (uint64_t)(((int)hpep->php_pfmem_start) << 16),
560 			    (uint64_t)(((int)hpep->php_pfmem_size) << 16));
561 			res_cnt++;
562 		}
563 	}
564 	return (res_cnt);
565 }
566 
567 static uchar_t *
568 find_sig(uchar_t *cp, int len, char *sig)
569 {
570 	long i;
571 
572 	/* Search for the "_MP_"  or "$HRT" signature */
573 	for (i = 0; i < len; i += 16) {
574 		if (cp[0] == sig[0] && cp[1] == sig[1] &&
575 		    cp[2] == sig[2] && cp[3] == sig[3])
576 			return (cp);
577 		cp += 16;
578 	}
579 	return (NULL);
580 }
581 
582 static int
583 checksum(unsigned char *cp, int len)
584 {
585 	int i;
586 	unsigned int cksum;
587 
588 	for (i = cksum = 0; i < len; i++)
589 		cksum += (unsigned int) *cp++;
590 
591 	return ((int)(cksum & 0xFF));
592 }
593 
594 #ifdef UNUSED_BUS_HIERARY_INFO
595 
596 /*
597  * At this point, the bus hierarchy entries do not appear to
598  * provide anything we can't find out from PCI config space.
599  * The only interesting bit is the ISA bus number, which we
600  * don't care.
601  */
602 int
603 mps_find_parent_bus(int bus)
604 {
605 	struct sasm *sasmp;
606 	uchar_t *extp;
607 
608 	if (mps_extp == NULL)
609 		return (-1);
610 
611 	extp = mps_extp;
612 	while (extp < mps_ext_endp) {
613 		bhdp = (struct bhd *)extp;
614 		switch (*extp) {
615 		case SYS_AS_MAPPING:
616 			extp += SYS_AS_MAPPING_SIZE;
617 			break;
618 		case BUS_HIERARCHY_DESC:
619 			if (bhdp->bhd_bus_id == bus)
620 				return (bhdp->bhd_parent);
621 			extp += BUS_HIERARCHY_DESC_SIZE;
622 			break;
623 		case COMP_BUS_AS_MODIFIER:
624 			extp += COMP_BUS_AS_MODIFIER_SIZE;
625 			break;
626 		default:
627 			cmn_err(CE_WARN, "Unknown descriptor type %d"
628 			    " in BIOS Multiprocessor Spec table.",
629 			    *extp);
630 			return (-1);
631 		}
632 	}
633 	return (-1);
634 }
635 
636 int
637 hrt_find_bus_range(int bus)
638 {
639 	int i, max_bus, sub_bus;
640 	struct php_entry *hpep;
641 
642 	if (hrt_hpep == NULL || hrt_entry_cnt == 0) {
643 		return (-1);
644 	}
645 	hpep = hrt_hpep;
646 	max_bus = -1;
647 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
648 		if (hpep->php_pri_bus != bus)
649 			continue;
650 		sub_bus = (int)hpep->php_subord_bus;
651 		if (sub_bus > max_bus)
652 			max_bus = sub_bus;
653 	}
654 	return (max_bus);
655 }
656 
657 #endif /* UNUSED_BUS_HIERARY_INFO */
658