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 *
find_bus_res(int bus,int type)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
acpi_pci_probe(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
acpi_trim_bus_ranges()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
acpi_find_bus_res(int bus,int type,struct memlist ** res)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
bus_res_fini(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 **
rlistpp(UINT8 t,UINT8 flags,int bus)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
acpi_wr_cb(ACPI_RESOURCE * rp,void * context)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
mps_probe()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
mps_find_bus_res(int bus,int type,struct memlist ** res)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 uint64_t base, len;
477
478 base = (uint64_t)sasmp->sasm_as_base |
479 (uint64_t)sasmp->sasm_as_base_hi << 32;
480 len = (uint64_t)sasmp->sasm_as_len |
481 (uint64_t)sasmp->sasm_as_len_hi << 32;
482 memlist_insert(res, base, 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
hrt_probe()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
hrt_find_bus_res(int bus,int type,struct memlist ** res)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 *
find_sig(uchar_t * cp,int len,char * sig)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
checksum(unsigned char * cp,int len)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
mps_find_parent_bus(int bus)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
hrt_find_bus_range(int bus)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