xref: /titanic_50/usr/src/uts/intel/io/pci/pci_resource.c (revision 8a3c961b6b8e22607c570d092514b791eb1519e9)
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 2008 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 
61 struct memlist *acpi_io_res[256];
62 struct memlist *acpi_mem_res[256];
63 struct memlist *acpi_pmem_res[256];
64 struct memlist *acpi_bus_res[256];
65 
66 /*
67  * -1 = attempt ACPI resource discovery
68  *  0 = don't attempt ACPI resource discovery
69  *  1 = ACPI resource discovery successful
70  */
71 volatile int acpi_resource_discovery = -1;
72 
73 struct memlist *
74 find_bus_res(int bus, int type)
75 {
76 	struct memlist *res = NULL;
77 
78 	if (tbl_init == 0) {
79 		tbl_init = 1;
80 		acpi_pci_probe();
81 		hrt_probe();
82 		mps_probe();
83 	}
84 
85 	if (acpi_find_bus_res(bus, type, &res) > 0)
86 		return (res);
87 
88 	if (hrt_find_bus_res(bus, type, &res) > 0)
89 		return (res);
90 
91 	(void) mps_find_bus_res(bus, type, &res);
92 	return (res);
93 }
94 
95 
96 static void
97 acpi_pci_probe(void)
98 {
99 	ACPI_HANDLE ah;
100 	dev_info_t *dip;
101 	int bus;
102 
103 	if (acpi_resource_discovery == 0)
104 		return;
105 
106 	for (bus = 0; bus < pci_bios_nbus; bus++) {
107 		/* if no dip or no ACPI handle, no resources to discover */
108 		dip = pci_bus_res[bus].dip;
109 		if ((dip == NULL) ||
110 		    (ACPI_FAILURE(acpica_get_handle(dip, &ah))))
111 			continue;
112 
113 		(void) AcpiWalkResources(ah, "_CRS", acpi_wr_cb,
114 		    (void *)(uintptr_t)bus);
115 	}
116 
117 	if (acpi_cb_cnt > 0)
118 		acpi_resource_discovery = 1;
119 }
120 
121 static int
122 acpi_find_bus_res(int bus, int type, struct memlist **res)
123 {
124 
125 	switch (type) {
126 	case IO_TYPE:
127 		*res = acpi_io_res[bus];
128 		break;
129 	case MEM_TYPE:
130 		*res = acpi_mem_res[bus];
131 		break;
132 	case PREFETCH_TYPE:
133 		*res = acpi_pmem_res[bus];
134 		break;
135 	case BUSRANGE_TYPE:
136 		*res = acpi_bus_res[bus];
137 		break;
138 	default:
139 		*res = NULL;
140 		break;
141 	}
142 
143 	/* memlist_count() treats NULL head as zero-length */
144 	return (memlist_count(*res));
145 }
146 
147 void
148 bus_res_fini(void)
149 {
150 	int bus;
151 
152 	for (bus = 0; bus < pci_bios_nbus; bus++) {
153 		memlist_free_all(&acpi_io_res[bus]);
154 		memlist_free_all(&acpi_mem_res[bus]);
155 		memlist_free_all(&acpi_pmem_res[bus]);
156 		memlist_free_all(&acpi_bus_res[bus]);
157 	}
158 }
159 
160 
161 struct memlist **
162 rlistpp(UINT8 t, UINT8 flags, int bus)
163 {
164 	switch (t) {
165 
166 		case ACPI_MEMORY_RANGE:
167 			/* is this really the best we've got? */
168 			if (((flags >> 1) & 0x3) == ACPI_PREFETCHABLE_MEMORY)
169 				return (&acpi_pmem_res[bus]);
170 			else
171 				return (&acpi_mem_res[bus]);
172 
173 		case ACPI_IO_RANGE:	return &acpi_io_res[bus];
174 		case ACPI_BUS_NUMBER_RANGE: return &acpi_bus_res[bus];
175 	}
176 	return ((struct memlist **)NULL);
177 }
178 
179 
180 ACPI_STATUS
181 acpi_wr_cb(ACPI_RESOURCE *rp, void *context)
182 {
183 	int bus = (intptr_t)context;
184 
185 	/* ignore consumed resources */
186 	if (rp->Data.Address.ProducerConsumer == 1)
187 		return (AE_OK);
188 
189 	switch (rp->Type) {
190 	case ACPI_RESOURCE_TYPE_IRQ:
191 		/* never expect to see a PCI bus produce an Interrupt */
192 		dprintf("%s\n", "IRQ");
193 		break;
194 
195 	case ACPI_RESOURCE_TYPE_DMA:
196 		/* never expect to see a PCI bus produce DMA */
197 		dprintf("%s\n", "DMA");
198 		break;
199 
200 	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
201 		dprintf("%s\n", "START_DEPENDENT");
202 		break;
203 
204 	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
205 		dprintf("%s\n", "END_DEPENDENT");
206 		break;
207 
208 	case ACPI_RESOURCE_TYPE_IO:
209 		if (rp->Data.Io.AddressLength == 0)
210 			break;
211 		acpi_cb_cnt++;
212 		memlist_insert(&acpi_io_res[bus], rp->Data.Io.Minimum,
213 		    rp->Data.Io.AddressLength);
214 		break;
215 
216 	case ACPI_RESOURCE_TYPE_FIXED_IO:
217 		/* only expect to see this as a consumer */
218 		dprintf("%s\n", "FIXED_IO");
219 		break;
220 
221 	case ACPI_RESOURCE_TYPE_VENDOR:
222 		dprintf("%s\n", "VENDOR");
223 		break;
224 
225 	case ACPI_RESOURCE_TYPE_END_TAG:
226 		dprintf("%s\n", "END_TAG");
227 		break;
228 
229 	case ACPI_RESOURCE_TYPE_MEMORY24:
230 		/* only expect to see this as a consumer */
231 		dprintf("%s\n", "MEMORY24");
232 		break;
233 
234 	case ACPI_RESOURCE_TYPE_MEMORY32:
235 		/* only expect to see this as a consumer */
236 		dprintf("%s\n", "MEMORY32");
237 		break;
238 
239 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
240 		/* only expect to see this as a consumer */
241 		dprintf("%s\n", "FIXED_MEMORY32");
242 		break;
243 
244 	case ACPI_RESOURCE_TYPE_ADDRESS16:
245 		if (rp->Data.Address16.AddressLength == 0)
246 			break;
247 		acpi_cb_cnt++;
248 		memlist_insert(rlistpp(rp->Data.Address16.ResourceType,
249 		    rp->Data.Address16.Info.TypeSpecific, bus),
250 		    rp->Data.Address16.Minimum,
251 		    rp->Data.Address16.AddressLength);
252 		break;
253 
254 	case ACPI_RESOURCE_TYPE_ADDRESS32:
255 		if (rp->Data.Address32.AddressLength == 0)
256 			break;
257 		acpi_cb_cnt++;
258 		memlist_insert(rlistpp(rp->Data.Address32.ResourceType,
259 		    rp->Data.Address32.Info.TypeSpecific, bus),
260 		    rp->Data.Address32.Minimum,
261 		    rp->Data.Address32.AddressLength);
262 		break;
263 
264 	case ACPI_RESOURCE_TYPE_ADDRESS64:
265 		if (rp->Data.Address64.AddressLength == 0)
266 			break;
267 		acpi_cb_cnt++;
268 		memlist_insert(rlistpp(rp->Data.Address64.ResourceType,
269 		    rp->Data.Address64.Info.TypeSpecific, bus),
270 		    rp->Data.Address64.Minimum,
271 		    rp->Data.Address64.AddressLength);
272 		break;
273 
274 	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
275 		if (rp->Data.ExtAddress64.AddressLength == 0)
276 			break;
277 		acpi_cb_cnt++;
278 		memlist_insert(rlistpp(rp->Data.ExtAddress64.ResourceType,
279 		    rp->Data.ExtAddress64.Info.TypeSpecific, bus),
280 		    rp->Data.ExtAddress64.Minimum,
281 		    rp->Data.ExtAddress64.AddressLength);
282 		break;
283 
284 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
285 		/* never expect to see a PCI bus produce an Interrupt */
286 		dprintf("%s\n", "EXTENDED_IRQ");
287 		break;
288 
289 	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
290 		/* never expect to see a PCI bus produce an GAS */
291 		dprintf("%s\n", "GENERIC_REGISTER");
292 		break;
293 	}
294 
295 	return (AE_OK);
296 }
297 
298 static void
299 mps_probe()
300 {
301 	uchar_t *extp;
302 	struct mps_fps_hdr *fpp = NULL;
303 	struct mps_ct_hdr *ctp;
304 	uintptr_t ebda_start, base_end;
305 	ushort_t ebda_seg, base_size, ext_len, base_len, base_end_seg;
306 
307 	base_size = *((ushort_t *)(0x413));
308 	ebda_seg = *((ushort_t *)(0x40e));
309 	ebda_start = ((uint32_t)ebda_seg) << 4;
310 	if (ebda_seg != 0) {
311 		fpp = (struct mps_fps_hdr *)find_sig(
312 		    (uchar_t *)ebda_start, 1024, "_MP_");
313 	}
314 	if (fpp == NULL) {
315 		base_end_seg = (base_size > 512) ? 0x9FC0 : 0x7FC0;
316 		if (base_end_seg != ebda_seg) {
317 			base_end = ((uintptr_t)base_end_seg) << 4;
318 			fpp = (struct mps_fps_hdr *)find_sig(
319 			    (uchar_t *)base_end, 1024, "_MP_");
320 		}
321 	}
322 	if (fpp == NULL) {
323 		fpp = (struct mps_fps_hdr *)find_sig(
324 		    (uchar_t *)0xF0000, 0x10000, "_MP_");
325 	}
326 
327 	if (fpp == NULL) {
328 		dprintf("MP Spec table doesn't exist");
329 		return;
330 	} else {
331 		dprintf("Found MP Floating Pointer Structure at %p\n",
332 		    (void *)fpp);
333 	}
334 
335 	if (checksum((uchar_t *)fpp, fpp->fps_len * 16) != 0) {
336 		dprintf("MP Floating Pointer Structure checksum error");
337 		return;
338 	}
339 
340 	ctp = (struct mps_ct_hdr *)(uintptr_t)fpp->fps_mpct_paddr;
341 	if (ctp->ct_sig != 0x504d4350) { /* check "PCMP" signature */
342 		dprintf("MP Configuration Table signature is wrong");
343 		return;
344 	}
345 
346 	base_len = ctp->ct_len;
347 	if (checksum((uchar_t *)ctp, base_len) != 0) {
348 		dprintf("MP Configuration Table checksum error");
349 		return;
350 	}
351 	if (ctp->ct_spec_rev != 4) { /* not MPSpec rev 1.4 */
352 		dprintf("MP Spec 1.1 found - extended table doesn't exist");
353 		return;
354 	}
355 	if ((ext_len = ctp->ct_ext_tbl_len) == 0) {
356 		dprintf("MP Spec 1.4 found - extended table doesn't exist");
357 		return;
358 	}
359 	extp = (uchar_t *)ctp + base_len;
360 	if (((checksum(extp, ext_len) + ctp->ct_ext_cksum) & 0xFF) != 0) {
361 		dprintf("MP Extended Table checksum error");
362 		return;
363 	}
364 	mps_extp = extp;
365 	mps_ext_endp = mps_extp + ext_len;
366 }
367 
368 
369 static int
370 mps_find_bus_res(int bus, int type, struct memlist **res)
371 {
372 	struct sasm *sasmp;
373 	uchar_t *extp;
374 	int res_cnt;
375 
376 	if (mps_extp == NULL)
377 		return (0);
378 	extp = mps_extp;
379 	res_cnt = 0;
380 	while (extp < mps_ext_endp) {
381 		switch (*extp) {
382 		case SYS_AS_MAPPING:
383 			sasmp = (struct sasm *)extp;
384 			if (((int)sasmp->sasm_as_type) == type &&
385 			    ((int)sasmp->sasm_bus_id) == bus) {
386 				if (sasmp->sasm_as_base_hi != 0 ||
387 				    sasmp->sasm_as_len_hi != 0) {
388 					printf("64 bits address space\n");
389 					extp += SYS_AS_MAPPING_SIZE;
390 					break;
391 				}
392 				memlist_insert(res,
393 				    (uint64_t)sasmp->sasm_as_base,
394 				    sasmp->sasm_as_len);
395 				res_cnt++;
396 			}
397 			extp += SYS_AS_MAPPING_SIZE;
398 			break;
399 		case BUS_HIERARCHY_DESC:
400 			extp += BUS_HIERARCHY_DESC_SIZE;
401 			break;
402 		case COMP_BUS_AS_MODIFIER:
403 			extp += COMP_BUS_AS_MODIFIER_SIZE;
404 			break;
405 		default:
406 			cmn_err(CE_WARN, "Unknown descriptor type %d"
407 			    " in BIOS Multiprocessor Spec table.",
408 			    *extp);
409 			while (*res) {
410 				struct memlist *tmp = *res;
411 				*res = tmp->next;
412 				memlist_free(tmp);
413 			}
414 			return (0);
415 		}
416 	}
417 	return (res_cnt);
418 }
419 
420 static void
421 hrt_probe()
422 {
423 	struct hrt_hdr *hrtp;
424 
425 	dprintf("search PCI Hot-Plug Resource Table starting at 0xF0000\n");
426 	if ((hrtp = (struct hrt_hdr *)find_sig((uchar_t *)0xF0000,
427 	    0x10000, "$HRT")) == NULL) {
428 		dprintf("NO PCI Hot-Plug Resource Table");
429 		return;
430 	}
431 	dprintf("Found PCI Hot-Plug Resource Table at %p\n", (void *)hrtp);
432 	if (hrtp->hrt_ver != 1) {
433 		dprintf("PCI Hot-Plug Resource Table version no. <> 1\n");
434 		return;
435 	}
436 	hrt_entry_cnt = (int)hrtp->hrt_entry_cnt;
437 	dprintf("No. of PCI hot-plug slot entries = 0x%x\n", hrt_entry_cnt);
438 	hrt_hpep = (struct php_entry *)(hrtp + 1);
439 }
440 
441 static int
442 hrt_find_bus_res(int bus, int type, struct memlist **res)
443 {
444 	int res_cnt, i;
445 	struct php_entry *hpep;
446 
447 	if (hrt_hpep == NULL || hrt_entry_cnt == 0)
448 		return (0);
449 	hpep = hrt_hpep;
450 	res_cnt = 0;
451 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
452 		if (hpep->php_pri_bus != bus)
453 			continue;
454 		if (type == IO_TYPE) {
455 			if (hpep->php_io_start == 0 || hpep->php_io_size == 0)
456 				continue;
457 			memlist_insert(res, (uint64_t)hpep->php_io_start,
458 			    (uint64_t)hpep->php_io_size);
459 			res_cnt++;
460 		} else if (type == MEM_TYPE) {
461 			if (hpep->php_mem_start == 0 || hpep->php_mem_size == 0)
462 				continue;
463 			memlist_insert(res,
464 			    (uint64_t)(((int)hpep->php_mem_start) << 16),
465 			    (uint64_t)(((int)hpep->php_mem_size) << 16));
466 			res_cnt++;
467 		} else if (type == PREFETCH_TYPE) {
468 			if (hpep->php_pfmem_start == 0 ||
469 			    hpep->php_pfmem_size == 0)
470 				continue;
471 			memlist_insert(res,
472 			    (uint64_t)(((int)hpep->php_pfmem_start) << 16),
473 			    (uint64_t)(((int)hpep->php_pfmem_size) << 16));
474 			res_cnt++;
475 		}
476 	}
477 	return (res_cnt);
478 }
479 
480 static uchar_t *
481 find_sig(uchar_t *cp, int len, char *sig)
482 {
483 	long i;
484 
485 	/* Search for the "_MP_"  or "$HRT" signature */
486 	for (i = 0; i < len; i += 16) {
487 		if (cp[0] == sig[0] && cp[1] == sig[1] &&
488 		    cp[2] == sig[2] && cp[3] == sig[3])
489 			return (cp);
490 		cp += 16;
491 	}
492 	return (NULL);
493 }
494 
495 static int
496 checksum(unsigned char *cp, int len)
497 {
498 	int i;
499 	unsigned int cksum;
500 
501 	for (i = cksum = 0; i < len; i++)
502 		cksum += (unsigned int) *cp++;
503 
504 	return ((int)(cksum & 0xFF));
505 }
506 
507 #ifdef UNUSED_BUS_HIERARY_INFO
508 
509 /*
510  * At this point, the bus hierarchy entries do not appear to
511  * provide anything we can't find out from PCI config space.
512  * The only interesting bit is the ISA bus number, which we
513  * don't care.
514  */
515 int
516 mps_find_parent_bus(int bus)
517 {
518 	struct sasm *sasmp;
519 	uchar_t *extp;
520 
521 	if (mps_extp == NULL)
522 		return (-1);
523 
524 	extp = mps_extp;
525 	while (extp < mps_ext_endp) {
526 		bhdp = (struct bhd *)extp;
527 		switch (*extp) {
528 		case SYS_AS_MAPPING:
529 			extp += SYS_AS_MAPPING_SIZE;
530 			break;
531 		case BUS_HIERARCHY_DESC:
532 			if (bhdp->bhd_bus_id == bus)
533 				return (bhdp->bhd_parent);
534 			extp += BUS_HIERARCHY_DESC_SIZE;
535 			break;
536 		case COMP_BUS_AS_MODIFIER:
537 			extp += COMP_BUS_AS_MODIFIER_SIZE;
538 			break;
539 		default:
540 			cmn_err(CE_WARN, "Unknown descriptor type %d"
541 			    " in BIOS Multiprocessor Spec table.",
542 			    *extp);
543 			return (-1);
544 		}
545 	}
546 	return (-1);
547 }
548 
549 int
550 hrt_find_bus_range(int bus)
551 {
552 	int i, max_bus, sub_bus;
553 	struct php_entry *hpep;
554 
555 	if (hrt_hpep == NULL || hrt_entry_cnt == 0) {
556 		return (-1);
557 	}
558 	hpep = hrt_hpep;
559 	max_bus = -1;
560 	for (i = 0; i < hrt_entry_cnt; i++, hpep++) {
561 		if (hpep->php_pri_bus != bus)
562 			continue;
563 		sub_bus = (int)hpep->php_subord_bus;
564 		if (sub_bus > max_bus)
565 			max_bus = sub_bus;
566 	}
567 	return (max_bus);
568 }
569 
570 #endif /* UNUSED_BUS_HIERARY_INFO */
571