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