1 /* $NetBSD: acpi_pci_link.c,v 1.29 2021/12/20 12:01:01 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: acpi_pci_link.c,v 1.29 2021/12/20 12:01:01 skrll Exp $");
31
32 #include <sys/param.h>
33 #include <sys/malloc.h>
34 #include <sys/queue.h>
35 #include <sys/reboot.h>
36 #include <sys/systm.h>
37
38 #include <dev/acpi/acpireg.h>
39 #include <dev/acpi/acpivar.h>
40
41 #include <dev/pci/pcireg.h>
42
43 #include "opt_acpi.h"
44
45
46 #define _COMPONENT ACPI_BUS_COMPONENT
47 ACPI_MODULE_NAME ("acpi_pci_link")
48
49 MALLOC_DECLARE(M_ACPI);
50
51 #define NUM_ISA_INTERRUPTS 16
52 #define NUM_ACPI_INTERRUPTS 256
53
54 #define PCI_INVALID_IRQ 255
55 #define PCI_INTERRUPT_VALID(x) ((x) != PCI_INVALID_IRQ && (x) != 0)
56
57 #define ACPI_SERIAL_BEGIN(x)
58 #define ACPI_SERIAL_END(x)
59
60 /*
61 * An ACPI PCI link device may contain multiple links. Each link has its
62 * own ACPI resource. _PRT entries specify which link is being used via
63 * the Source Index.
64 *
65 * XXX: A note about Source Indices and DPFs: Currently we assume that
66 * the DPF start and end tags are not counted towards the index that
67 * Source Index corresponds to. Also, we assume that when DPFs are in use
68 * they various sets overlap in terms of Indices. Here's an example
69 * resource list indicating these assumptions:
70 *
71 * Resource Index
72 * -------- -----
73 * I/O Port 0
74 * Start DPF -
75 * IRQ 1
76 * MemIO 2
77 * Start DPF -
78 * IRQ 1
79 * MemIO 2
80 * End DPF -
81 * DMA Channel 3
82 *
83 * The XXX is because I'm not sure if this is a valid assumption to make.
84 */
85
86 /* States during DPF processing. */
87 #define DPF_OUTSIDE 0
88 #define DPF_FIRST 1
89 #define DPF_IGNORE 2
90
91 struct link;
92
93 struct acpi_pci_link_softc {
94 int pl_num_links;
95 int pl_crs_bad;
96 struct link *pl_links;
97 char pl_name[32];
98 ACPI_HANDLE pl_handle;
99 TAILQ_ENTRY(acpi_pci_link_softc) pl_list;
100 };
101
102 static TAILQ_HEAD(, acpi_pci_link_softc) acpi_pci_linkdevs =
103 TAILQ_HEAD_INITIALIZER(acpi_pci_linkdevs);
104
105
106 struct link {
107 struct acpi_pci_link_softc *l_sc;
108 uint8_t l_bios_irq;
109 uint8_t l_irq;
110 uint8_t l_trig;
111 uint8_t l_pol;
112 uint8_t l_initial_irq;
113 int l_res_index;
114 int l_num_irqs;
115 int *l_irqs;
116 int l_references;
117 int l_dev_count;
118 pcitag_t *l_devices;
119 u_int l_routed:1;
120 u_int l_isa_irq:1;
121 ACPI_RESOURCE l_prs_template;
122 };
123
124 struct link_count_request {
125 int in_dpf;
126 int count;
127 };
128
129 struct link_res_request {
130 struct acpi_pci_link_softc *sc;
131 int in_dpf;
132 int res_index;
133 int link_index;
134 };
135
136 static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
137 static int pci_link_bios_isa_irqs;
138
139 static ACPI_STATUS acpi_count_irq_resources(ACPI_RESOURCE *, void *);
140 static ACPI_STATUS link_add_crs(ACPI_RESOURCE *, void *);
141 static ACPI_STATUS link_add_prs(ACPI_RESOURCE *, void *);
142 static int link_valid_irq(struct link *, int);
143 static void acpi_pci_link_dump(struct acpi_pci_link_softc *);
144 static int acpi_pci_link_attach(struct acpi_pci_link_softc *);
145 static uint8_t acpi_pci_link_search_irq(struct acpi_pci_link_softc *,
146 pci_chipset_tag_t, int, int, int);
147 static struct link *acpi_pci_link_lookup(struct acpi_pci_link_softc *, int);
148 static ACPI_STATUS acpi_pci_link_srs(struct acpi_pci_link_softc *,
149 ACPI_BUFFER *);
150 static ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *, ACPI_RESOURCE *);
151
152 static ACPI_STATUS
acpi_count_irq_resources(ACPI_RESOURCE * res,void * context)153 acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
154 {
155 struct link_count_request *req;
156
157 req = (struct link_count_request *)context;
158 switch (res->Type) {
159 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
160 switch (req->in_dpf) {
161 case DPF_OUTSIDE:
162 /* We've started the first DPF. */
163 req->in_dpf = DPF_FIRST;
164 break;
165 case DPF_FIRST:
166 /* We've started the second DPF. */
167 req->in_dpf = DPF_IGNORE;
168 break;
169 }
170 break;
171 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
172 /* We are finished with DPF parsing. */
173 KASSERT(req->in_dpf != DPF_OUTSIDE);
174 req->in_dpf = DPF_OUTSIDE;
175 break;
176 case ACPI_RESOURCE_TYPE_IRQ:
177 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
178 /*
179 * Don't count resources if we are in a DPF set that we are
180 * ignoring.
181 */
182 if (req->in_dpf != DPF_IGNORE)
183 req->count++;
184 }
185 return AE_OK;
186 }
187
188 static ACPI_STATUS
link_add_crs(ACPI_RESOURCE * res,void * context)189 link_add_crs(ACPI_RESOURCE *res, void *context)
190 {
191 struct link_res_request *req;
192 struct link *link;
193
194 req = (struct link_res_request *)context;
195 switch (res->Type) {
196 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
197 switch (req->in_dpf) {
198 case DPF_OUTSIDE:
199 /* We've started the first DPF. */
200 req->in_dpf = DPF_FIRST;
201 break;
202 case DPF_FIRST:
203 /* We've started the second DPF. */
204 panic(
205 "%s: Multiple dependent functions within a current resource",
206 __func__);
207 break;
208 }
209 break;
210 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
211 /* We are finished with DPF parsing. */
212 KASSERT(req->in_dpf != DPF_OUTSIDE);
213 req->in_dpf = DPF_OUTSIDE;
214 break;
215 case ACPI_RESOURCE_TYPE_IRQ:
216 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
217 KASSERT(req->link_index < req->sc->pl_num_links);
218 link = &req->sc->pl_links[req->link_index];
219 link->l_res_index = req->res_index;
220 req->link_index++;
221 req->res_index++;
222
223 /*
224 * Only use the current value if there's one IRQ. Some
225 * systems return multiple IRQs (which is nonsense for _CRS)
226 * when the link hasn't been programmed.
227 */
228 if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
229 if (res->Data.Irq.InterruptCount == 1) {
230 link->l_irq = res->Data.Irq.Interrupts[0];
231 link->l_trig = res->Data.Irq.Triggering;
232 link->l_pol = res->Data.Irq.Polarity;
233 }
234 } else if (res->Data.ExtendedIrq.InterruptCount == 1) {
235 link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
236 link->l_trig = res->Data.ExtendedIrq.Triggering;
237 link->l_pol = res->Data.ExtendedIrq.Polarity;
238 }
239
240 /*
241 * An IRQ of zero means that the link isn't routed.
242 */
243 if (link->l_irq == 0)
244 link->l_irq = PCI_INVALID_IRQ;
245 break;
246 default:
247 req->res_index++;
248 }
249 return AE_OK;
250 }
251
252 /*
253 * Populate the set of possible IRQs for each device.
254 */
255 static ACPI_STATUS
link_add_prs(ACPI_RESOURCE * res,void * context)256 link_add_prs(ACPI_RESOURCE *res, void *context)
257 {
258 ACPI_RESOURCE *tmp;
259 struct link_res_request *req;
260 struct link *link;
261 uint8_t *irqs = NULL;
262 uint32_t *ext_irqs = NULL;
263 int i, is_ext_irq = 1;
264
265 req = (struct link_res_request *)context;
266 switch (res->Type) {
267 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
268 switch (req->in_dpf) {
269 case DPF_OUTSIDE:
270 /* We've started the first DPF. */
271 req->in_dpf = DPF_FIRST;
272 break;
273 case DPF_FIRST:
274 /* We've started the second DPF. */
275 req->in_dpf = DPF_IGNORE;
276 break;
277 }
278 break;
279 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
280 /* We are finished with DPF parsing. */
281 KASSERT(req->in_dpf != DPF_OUTSIDE);
282 req->in_dpf = DPF_OUTSIDE;
283 break;
284 case ACPI_RESOURCE_TYPE_IRQ:
285 is_ext_irq = 0;
286 /* fall through */
287 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
288 /*
289 * Don't parse resources if we are in a DPF set that we are
290 * ignoring.
291 */
292 if (req->in_dpf == DPF_IGNORE)
293 break;
294
295 KASSERT(req->link_index < req->sc->pl_num_links);
296 link = &req->sc->pl_links[req->link_index];
297 if (link->l_res_index == -1) {
298 KASSERT(req->sc->pl_crs_bad);
299 link->l_res_index = req->res_index;
300 }
301 req->link_index++;
302 req->res_index++;
303
304 /*
305 * Stash a copy of the resource for later use when doing
306 * _SRS.
307 */
308 tmp = &link->l_prs_template;
309 if (is_ext_irq) {
310 memcpy(tmp, res, ACPI_RS_SIZE(tmp->Data.ExtendedIrq));
311
312 /*
313 * XXX acpi_AppendBufferResource() cannot handle
314 * optional data.
315 */
316 memset(&tmp->Data.ExtendedIrq.ResourceSource, 0,
317 sizeof(tmp->Data.ExtendedIrq.ResourceSource));
318 tmp->Length = ACPI_RS_SIZE(tmp->Data.ExtendedIrq);
319
320 link->l_num_irqs =
321 res->Data.ExtendedIrq.InterruptCount;
322 link->l_trig = res->Data.ExtendedIrq.Triggering;
323 link->l_pol = res->Data.ExtendedIrq.Polarity;
324 ext_irqs = res->Data.ExtendedIrq.Interrupts;
325 } else {
326 memcpy(tmp, res, ACPI_RS_SIZE(tmp->Data.Irq));
327 link->l_num_irqs = res->Data.Irq.InterruptCount;
328 link->l_trig = res->Data.Irq.Triggering;
329 link->l_pol = res->Data.Irq.Polarity;
330 irqs = res->Data.Irq.Interrupts;
331 }
332 if (link->l_num_irqs == 0)
333 break;
334
335 /*
336 * Save a list of the valid IRQs. Also, if all of the
337 * valid IRQs are ISA IRQs, then mark this link as
338 * routed via an ISA interrupt.
339 */
340 link->l_isa_irq = TRUE;
341 link->l_irqs = malloc(sizeof(int) * link->l_num_irqs,
342 M_ACPI, M_WAITOK | M_ZERO);
343 for (i = 0; i < link->l_num_irqs; i++) {
344 if (is_ext_irq) {
345 link->l_irqs[i] = ext_irqs[i];
346 if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
347 link->l_isa_irq = FALSE;
348 } else {
349 link->l_irqs[i] = irqs[i];
350 if (irqs[i] >= NUM_ISA_INTERRUPTS)
351 link->l_isa_irq = FALSE;
352 }
353 }
354 break;
355 default:
356 if (req->in_dpf == DPF_IGNORE)
357 break;
358 if (req->sc->pl_crs_bad)
359 aprint_normal("%s: Warning: possible resource %d "
360 "will be lost during _SRS\n", req->sc->pl_name,
361 req->res_index);
362 req->res_index++;
363 }
364 return AE_OK;
365 }
366
367 static int
link_valid_irq(struct link * link,int irq)368 link_valid_irq(struct link *link, int irq)
369 {
370 int i;
371
372 /* Invalid interrupts are never valid. */
373 if (!PCI_INTERRUPT_VALID(irq))
374 return FALSE;
375
376 /* Any interrupt in the list of possible interrupts is valid. */
377 for (i = 0; i < link->l_num_irqs; i++)
378 if (link->l_irqs[i] == irq)
379 return TRUE;
380
381 /*
382 * For links routed via an ISA interrupt, if the SCI is routed via
383 * an ISA interrupt, the SCI is always treated as a valid IRQ.
384 */
385 if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
386 irq < NUM_ISA_INTERRUPTS)
387 return TRUE;
388
389 /* If the interrupt wasn't found in the list it is not valid. */
390 return FALSE;
391 }
392
393 void
acpi_pci_link_state(void)394 acpi_pci_link_state(void)
395 {
396 struct acpi_pci_link_softc *sc;
397
398 TAILQ_FOREACH(sc, &acpi_pci_linkdevs, pl_list) {
399 acpi_pci_link_dump(sc);
400 }
401 }
402
403 static void
acpi_pci_link_dump(struct acpi_pci_link_softc * sc)404 acpi_pci_link_dump(struct acpi_pci_link_softc *sc)
405 {
406 struct link *link;
407 int i, j;
408
409 printf("Link Device %s:\n", sc->pl_name);
410 printf("Index IRQ Rtd Ref IRQs\n");
411 for (i = 0; i < sc->pl_num_links; i++) {
412 link = &sc->pl_links[i];
413 printf("%5d %3d %c %3d ", i, link->l_irq,
414 link->l_routed ? 'Y' : 'N', link->l_references);
415 if (link->l_num_irqs == 0)
416 printf(" none");
417 else for (j = 0; j < link->l_num_irqs; j++)
418 printf(" %d", link->l_irqs[j]);
419 printf(" polarity %u trigger %u\n", link->l_pol, link->l_trig);
420 }
421 printf("\n");
422 }
423
424 static int
acpi_pci_link_attach(struct acpi_pci_link_softc * sc)425 acpi_pci_link_attach(struct acpi_pci_link_softc *sc)
426 {
427 struct link_count_request creq;
428 struct link_res_request rreq;
429 ACPI_STATUS status;
430 int i;
431
432 ACPI_SERIAL_BEGIN(pci_link);
433
434 /*
435 * Count the number of current resources so we know how big of
436 * a link array to allocate. On some systems, _CRS is broken,
437 * so for those systems try to derive the count from _PRS instead.
438 */
439 creq.in_dpf = DPF_OUTSIDE;
440 creq.count = 0;
441 status = AcpiWalkResources(sc->pl_handle, "_CRS",
442 acpi_count_irq_resources, &creq);
443 sc->pl_crs_bad = ACPI_FAILURE(status);
444 if (sc->pl_crs_bad) {
445 creq.in_dpf = DPF_OUTSIDE;
446 creq.count = 0;
447 status = AcpiWalkResources(sc->pl_handle, "_PRS",
448 acpi_count_irq_resources, &creq);
449 if (ACPI_FAILURE(status)) {
450 aprint_error("%s: Unable to parse _CRS or _PRS: %s\n",
451 sc->pl_name, AcpiFormatException(status));
452 ACPI_SERIAL_END(pci_link);
453 return ENXIO;
454 }
455 }
456 sc->pl_num_links = creq.count;
457 if (creq.count == 0) {
458 ACPI_SERIAL_END(pci_link);
459 return 0;
460 }
461 sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
462 M_ACPI, M_WAITOK | M_ZERO);
463
464 /* Initialize the child links. */
465 for (i = 0; i < sc->pl_num_links; i++) {
466 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
467 sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
468 sc->pl_links[i].l_sc = sc;
469 sc->pl_links[i].l_isa_irq = FALSE;
470 sc->pl_links[i].l_res_index = -1;
471 sc->pl_links[i].l_dev_count = 0;
472 sc->pl_links[i].l_devices = NULL;
473 }
474
475 /* Try to read the current settings from _CRS if it is valid. */
476 if (!sc->pl_crs_bad) {
477 rreq.in_dpf = DPF_OUTSIDE;
478 rreq.link_index = 0;
479 rreq.res_index = 0;
480 rreq.sc = sc;
481 status = AcpiWalkResources(sc->pl_handle, "_CRS",
482 link_add_crs, &rreq);
483 if (ACPI_FAILURE(status)) {
484 aprint_error("%s: Unable to parse _CRS: %s\n",
485 sc->pl_name, AcpiFormatException(status));
486 goto fail;
487 }
488 }
489
490 /*
491 * Try to read the possible settings from _PRS. Note that if the
492 * _CRS is toast, we depend on having a working _PRS. However, if
493 * _CRS works, then it is ok for _PRS to be missing.
494 */
495 rreq.in_dpf = DPF_OUTSIDE;
496 rreq.link_index = 0;
497 rreq.res_index = 0;
498 rreq.sc = sc;
499 status = AcpiWalkResources(sc->pl_handle, "_PRS",
500 link_add_prs, &rreq);
501 if (ACPI_FAILURE(status) &&
502 (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
503 aprint_error("%s: Unable to parse _PRS: %s\n",
504 sc->pl_name, AcpiFormatException(status));
505 goto fail;
506 }
507 if (boothowto & AB_VERBOSE) {
508 aprint_normal("%s: Links after initial probe:\n", sc->pl_name);
509 acpi_pci_link_dump(sc);
510 }
511
512 /* Verify initial IRQs if we have _PRS. */
513 if (status != AE_NOT_FOUND)
514 for (i = 0; i < sc->pl_num_links; i++)
515 if (!link_valid_irq(&sc->pl_links[i],
516 sc->pl_links[i].l_irq))
517 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
518 if (boothowto & AB_VERBOSE) {
519 printf("%s: Links after initial validation:\n", sc->pl_name);
520 acpi_pci_link_dump(sc);
521 }
522
523 /* Save initial IRQs. */
524 for (i = 0; i < sc->pl_num_links; i++)
525 sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
526
527 /*
528 * Try to disable this link. If successful, set the current IRQ to
529 * zero and flags to indicate this link is not routed. If we can't
530 * run _DIS (i.e., the method doesn't exist), assume the initial
531 * IRQ was routed by the BIOS.
532 */
533 #ifndef ACPI__DIS_IS_BROKEN
534 if (ACPI_SUCCESS(AcpiEvaluateObject(sc->pl_handle, "_DIS", NULL,
535 NULL)))
536 for (i = 0; i < sc->pl_num_links; i++)
537 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
538 else
539 #endif
540 for (i = 0; i < sc->pl_num_links; i++)
541 if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
542 sc->pl_links[i].l_routed = TRUE;
543 if (boothowto & AB_VERBOSE) {
544 printf("%s: Links after disable:\n", sc->pl_name);
545 acpi_pci_link_dump(sc);
546 }
547 ACPI_SERIAL_END(pci_link);
548 return 0;
549 fail:
550 ACPI_SERIAL_END(pci_link);
551 for (i = 0; i < sc->pl_num_links; i++) {
552 if (sc->pl_links[i].l_irqs != NULL)
553 free(sc->pl_links[i].l_irqs, M_ACPI);
554 if (sc->pl_links[i].l_devices != NULL)
555 free(sc->pl_links[i].l_devices, M_ACPI);
556 }
557 free(sc->pl_links, M_ACPI);
558 return ENXIO;
559 }
560
561 static void
acpi_pci_link_add_functions(struct acpi_pci_link_softc * sc,struct link * link,pci_chipset_tag_t pc,int bus,int device,int pin)562 acpi_pci_link_add_functions(struct acpi_pci_link_softc *sc, struct link *link,
563 pci_chipset_tag_t pc, int bus, int device, int pin)
564 {
565 uint32_t value;
566 uint8_t func, maxfunc, ipin;
567 pcitag_t tag;
568
569 tag = pci_make_tag(pc, bus, device, 0);
570 /* See if we have a valid device at function 0. */
571 value = pci_conf_read(pc, tag, PCI_BHLC_REG);
572 if (PCI_HDRTYPE_TYPE(value) > PCI_HDRTYPE_PCB)
573 return;
574 if (PCI_HDRTYPE_MULTIFN(value))
575 maxfunc = 7;
576 else
577 maxfunc = 0;
578
579 /* Scan all possible functions at this device. */
580 for (func = 0; func <= maxfunc; func++) {
581 tag = pci_make_tag(pc, bus, device, func);
582 value = pci_conf_read(pc, tag, PCI_ID_REG);
583 if (PCI_VENDOR(value) == 0xffff)
584 continue;
585 value = pci_conf_read(pc, tag,
586 PCI_INTERRUPT_REG);
587 ipin = PCI_INTERRUPT_PIN(value);
588 /*
589 * See if it uses the pin in question. Note that the passed
590 * in pin uses 0 for A, .. 3 for D whereas the intpin
591 * register uses 0 for no interrupt, 1 for A, .. 4 for D.
592 */
593 if (ipin != pin + 1)
594 continue;
595
596 link->l_devices = realloc(link->l_devices,
597 sizeof(pcitag_t) * (link->l_dev_count + 1),
598 M_ACPI, M_WAITOK);
599 link->l_devices[link->l_dev_count] = tag;
600 ++link->l_dev_count;
601 }
602 }
603
604 static uint8_t
acpi_pci_link_search_irq(struct acpi_pci_link_softc * sc,pci_chipset_tag_t pc,int bus,int device,int pin)605 acpi_pci_link_search_irq(struct acpi_pci_link_softc *sc, pci_chipset_tag_t pc,
606 int bus, int device, int pin)
607 {
608 uint32_t value;
609 uint8_t func, maxfunc, ipin, iline;
610 pcitag_t tag;
611
612 tag = pci_make_tag(pc, bus, device, 0);
613 /* See if we have a valid device at function 0. */
614 value = pci_conf_read(pc, tag, PCI_BHLC_REG);
615 if (PCI_HDRTYPE_TYPE(value) > PCI_HDRTYPE_PCB)
616 return PCI_INVALID_IRQ;
617 if (PCI_HDRTYPE_MULTIFN(value))
618 maxfunc = 7;
619 else
620 maxfunc = 0;
621
622 /* Scan all possible functions at this device. */
623 for (func = 0; func <= maxfunc; func++) {
624 tag = pci_make_tag(pc, bus, device, func);
625 value = pci_conf_read(pc, tag, PCI_ID_REG);
626 if (PCI_VENDOR(value) == 0xffff)
627 continue;
628 value = pci_conf_read(pc, tag,
629 PCI_INTERRUPT_REG);
630 ipin = PCI_INTERRUPT_PIN(value);
631 iline = PCI_INTERRUPT_LINE(value);
632
633 /*
634 * See if it uses the pin in question. Note that the passed
635 * in pin uses 0 for A, .. 3 for D whereas the intpin
636 * register uses 0 for no interrupt, 1 for A, .. 4 for D.
637 */
638 if (ipin != pin + 1)
639 continue;
640 aprint_verbose(
641 "%s: ACPI: Found matching pin for %d.%d.INT%c"
642 " at func %d: %d\n",
643 sc->pl_name, bus, device, pin + 'A', func, iline);
644 if (PCI_INTERRUPT_VALID(iline))
645 return iline;
646 }
647 return PCI_INVALID_IRQ;
648 }
649
650 /*
651 * Find the link structure that corresponds to the resource index passed in
652 * via 'source_index'.
653 */
654 static struct link *
acpi_pci_link_lookup(struct acpi_pci_link_softc * sc,int source_index)655 acpi_pci_link_lookup(struct acpi_pci_link_softc *sc, int source_index)
656 {
657 int i;
658
659 for (i = 0; i < sc->pl_num_links; i++)
660 if (sc->pl_links[i].l_res_index == source_index)
661 return &sc->pl_links[i];
662 return NULL;
663 }
664
665 void
acpi_pci_link_add_reference(void * v,pci_chipset_tag_t pc,int index,int bus,int slot,int pin)666 acpi_pci_link_add_reference(void *v, pci_chipset_tag_t pc, int index,
667 int bus, int slot, int pin)
668 {
669 struct acpi_pci_link_softc *sc = v;
670 struct link *link;
671 uint8_t bios_irq;
672
673 /* Bump the reference count. */
674 ACPI_SERIAL_BEGIN(pci_link);
675 link = acpi_pci_link_lookup(sc, index);
676 if (link == NULL) {
677 printf("%s: apparently invalid index %d\n", sc->pl_name, index);
678 ACPI_SERIAL_END(pci_link);
679 return;
680 }
681 link->l_references++;
682 acpi_pci_link_add_functions(sc, link, pc, bus, slot, pin);
683 if (link->l_routed)
684 pci_link_interrupt_weights[link->l_irq]++;
685
686 /*
687 * The BIOS only routes interrupts via ISA IRQs using the ATPICs
688 * (8259As). Thus, if this link is routed via an ISA IRQ, go
689 * look to see if the BIOS routed an IRQ for this link at the
690 * indicated (bus, slot, pin). If so, we prefer that IRQ for
691 * this link and add that IRQ to our list of known-good IRQs.
692 * This provides a good work-around for link devices whose _CRS
693 * method is either broken or bogus. We only use the value
694 * returned by _CRS if we can't find a valid IRQ via this method
695 * in fact.
696 *
697 * If this link is not routed via an ISA IRQ (because we are using
698 * APIC for example), then don't bother looking up the BIOS IRQ
699 * as if we find one it won't be valid anyway.
700 */
701 if (!link->l_isa_irq) {
702 ACPI_SERIAL_END(pci_link);
703 return;
704 }
705
706 /* Try to find a BIOS IRQ setting from any matching devices. */
707 bios_irq = acpi_pci_link_search_irq(sc, pc, bus, slot, pin);
708 if (!PCI_INTERRUPT_VALID(bios_irq)) {
709 ACPI_SERIAL_END(pci_link);
710 return;
711 }
712
713 /* Validate the BIOS IRQ. */
714 if (!link_valid_irq(link, bios_irq)) {
715 printf("%s: BIOS IRQ %u for %d.%d.INT%c is invalid\n",
716 sc->pl_name, bios_irq, (int)bus, slot, pin + 'A');
717 } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
718 link->l_bios_irq = bios_irq;
719 if (bios_irq < NUM_ISA_INTERRUPTS)
720 pci_link_bios_isa_irqs |= (1 << bios_irq);
721 if (bios_irq != link->l_initial_irq &&
722 PCI_INTERRUPT_VALID(link->l_initial_irq))
723 printf(
724 "%s: BIOS IRQ %u does not match initial IRQ %u\n",
725 sc->pl_name, bios_irq, link->l_initial_irq);
726 } else if (bios_irq != link->l_bios_irq)
727 printf(
728 "%s: BIOS IRQ %u for %d.%d.INT%c does not match "
729 "previous BIOS IRQ %u\n",
730 sc->pl_name, bios_irq, (int)bus, slot, pin + 'A',
731 link->l_bios_irq);
732 ACPI_SERIAL_END(pci_link);
733 }
734
735 static ACPI_STATUS
acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc * sc,ACPI_BUFFER * srsbuf)736 acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
737 {
738 ACPI_RESOURCE *end, *res;
739 ACPI_STATUS status;
740 struct link *link;
741 int i, in_dpf;
742
743 /* Fetch the _CRS. */
744 srsbuf->Pointer = NULL;
745 srsbuf->Length = ACPI_ALLOCATE_BUFFER;
746 status = AcpiGetCurrentResources(sc->pl_handle, srsbuf);
747 if (ACPI_SUCCESS(status) && srsbuf->Pointer == NULL)
748 status = AE_NO_MEMORY;
749 if (ACPI_FAILURE(status)) {
750 aprint_verbose("%s: Unable to fetch current resources: %s\n",
751 sc->pl_name, AcpiFormatException(status));
752 return status;
753 }
754
755 /* Fill in IRQ resources via link structures. */
756 link = sc->pl_links;
757 i = 0;
758 in_dpf = DPF_OUTSIDE;
759 res = (ACPI_RESOURCE *)srsbuf->Pointer;
760 end = (ACPI_RESOURCE *)((char *)srsbuf->Pointer + srsbuf->Length);
761 for (;;) {
762 switch (res->Type) {
763 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
764 switch (in_dpf) {
765 case DPF_OUTSIDE:
766 /* We've started the first DPF. */
767 in_dpf = DPF_FIRST;
768 break;
769 case DPF_FIRST:
770 /* We've started the second DPF. */
771 panic(
772 "%s: Multiple dependent functions within a current resource",
773 __func__);
774 break;
775 }
776 break;
777 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
778 /* We are finished with DPF parsing. */
779 KASSERT(in_dpf != DPF_OUTSIDE);
780 in_dpf = DPF_OUTSIDE;
781 break;
782 case ACPI_RESOURCE_TYPE_IRQ:
783 res->Data.Irq.InterruptCount = 1;
784 if (PCI_INTERRUPT_VALID(link->l_irq)) {
785 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS);
786 res->Data.Irq.Interrupts[0] = link->l_irq;
787 res->Data.Irq.Triggering = link->l_trig;
788 res->Data.Irq.Polarity = link->l_pol;
789 } else
790 res->Data.Irq.Interrupts[0] = 0;
791 link++;
792 i++;
793 break;
794 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
795 res->Data.ExtendedIrq.InterruptCount = 1;
796 if (PCI_INTERRUPT_VALID(link->l_irq)) {
797 res->Data.ExtendedIrq.Interrupts[0] =
798 link->l_irq;
799 res->Data.ExtendedIrq.Triggering =
800 link->l_trig;
801 res->Data.ExtendedIrq.Polarity = link->l_pol;
802 } else
803 res->Data.ExtendedIrq.Interrupts[0] = 0;
804 link++;
805 i++;
806 break;
807 }
808 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
809 break;
810 res = ACPI_NEXT_RESOURCE(res);
811 if (res >= end)
812 break;
813 }
814 return AE_OK;
815 }
816
817 static ACPI_STATUS
acpi_pci_link_srs_from_links(struct acpi_pci_link_softc * sc,ACPI_BUFFER * srsbuf)818 acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
819 ACPI_BUFFER *srsbuf)
820 {
821 ACPI_RESOURCE newres;
822 ACPI_STATUS status;
823 struct link *link;
824 int i;
825
826 /* Start off with an empty buffer. */
827 srsbuf->Pointer = NULL;
828 link = sc->pl_links;
829 for (i = 0; i < sc->pl_num_links; i++) {
830
831 /* Add a new IRQ resource from each link. */
832 link = &sc->pl_links[i];
833 if (link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ) {
834
835 /* Build an IRQ resource. */
836 bcopy(&link->l_prs_template, &newres,
837 ACPI_RS_SIZE(newres.Data.Irq));
838 newres.Data.Irq.InterruptCount = 1;
839 if (PCI_INTERRUPT_VALID(link->l_irq)) {
840 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS);
841 newres.Data.Irq.Interrupts[0] = link->l_irq;
842 newres.Data.Irq.Triggering = link->l_trig;
843 newres.Data.Irq.Polarity = link->l_pol;
844 } else
845 newres.Data.Irq.Interrupts[0] = 0;
846 } else {
847
848 /* Build an ExtIRQ resuorce. */
849 bcopy(&link->l_prs_template, &newres,
850 ACPI_RS_SIZE(newres.Data.ExtendedIrq));
851 newres.Data.ExtendedIrq.InterruptCount = 1;
852 if (PCI_INTERRUPT_VALID(link->l_irq)) {
853 newres.Data.ExtendedIrq.Interrupts[0] =
854 link->l_irq;
855 newres.Data.ExtendedIrq.Triggering =
856 link->l_trig;
857 newres.Data.ExtendedIrq.Polarity =
858 link->l_pol;
859 } else {
860 newres.Data.ExtendedIrq.Interrupts[0] = 0;
861 }
862 }
863
864 /* Add the new resource to the end of the _SRS buffer. */
865 status = acpi_AppendBufferResource(srsbuf, &newres);
866 if (ACPI_FAILURE(status)) {
867 printf("%s: Unable to build resources: %s\n",
868 sc->pl_name, AcpiFormatException(status));
869 if (srsbuf->Pointer != NULL)
870 ACPI_FREE(srsbuf->Pointer);
871 return status;
872 }
873 }
874 return AE_OK;
875 }
876
877 static ACPI_STATUS
acpi_pci_link_srs(struct acpi_pci_link_softc * sc,ACPI_BUFFER * srsbuf)878 acpi_pci_link_srs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
879 {
880 ACPI_STATUS status;
881
882 if (sc->pl_crs_bad)
883 status = acpi_pci_link_srs_from_links(sc, srsbuf);
884 else
885 status = acpi_pci_link_srs_from_crs(sc, srsbuf);
886
887 if (ACPI_FAILURE(status))
888 printf("%s: Unable to find link srs : %s\n",
889 sc->pl_name, AcpiFormatException(status));
890
891 /* Write out new resources via _SRS. */
892 return AcpiSetCurrentResources(sc->pl_handle, srsbuf);
893 }
894
895 static ACPI_STATUS
acpi_pci_link_route_irqs(struct acpi_pci_link_softc * sc,int * irq,int * pol,int * trig)896 acpi_pci_link_route_irqs(struct acpi_pci_link_softc *sc, int *irq, int *pol,
897 int *trig)
898 {
899 ACPI_RESOURCE *resource, *end;
900 ACPI_BUFFER srsbuf;
901 ACPI_STATUS status;
902 struct link *link;
903 int i, is_ext = 0;
904
905 status = acpi_pci_link_srs(sc, &srsbuf);
906 if (ACPI_FAILURE(status)) {
907 printf("%s: _SRS failed: %s\n",
908 sc->pl_name, AcpiFormatException(status));
909 return status;
910 }
911 /*
912 * Perform acpi_config_intr() on each IRQ resource if it was just
913 * routed for the first time.
914 */
915 link = sc->pl_links;
916 i = 0;
917 resource = (ACPI_RESOURCE *)srsbuf.Pointer;
918 end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
919 for (;;) {
920 if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
921 break;
922 switch (resource->Type) {
923 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
924 is_ext = 1;
925 /* FALLTHROUGH */
926 case ACPI_RESOURCE_TYPE_IRQ:
927 /*
928 * Only configure the interrupt and update the
929 * weights if this link has a valid IRQ and was
930 * previously unrouted.
931 */
932 if (!link->l_routed &&
933 PCI_INTERRUPT_VALID(link->l_irq)) {
934 *trig = is_ext ?
935 resource->Data.ExtendedIrq.Triggering :
936 resource->Data.Irq.Triggering;
937 *pol = is_ext ?
938 resource->Data.ExtendedIrq.Polarity :
939 resource->Data.Irq.Polarity;
940 *irq = is_ext ?
941 resource->Data.ExtendedIrq.Interrupts[0] :
942 resource->Data.Irq.Interrupts[0];
943 link->l_routed = TRUE;
944 pci_link_interrupt_weights[link->l_irq] +=
945 link->l_references;
946 }
947 link++;
948 i++;
949 break;
950 }
951 resource = ACPI_NEXT_RESOURCE(resource);
952 if (resource >= end)
953 break;
954 }
955 ACPI_FREE(srsbuf.Pointer);
956 return AE_OK;
957 }
958
959 /*
960 * Pick an IRQ to use for this unrouted link.
961 */
962 static uint8_t
acpi_pci_link_choose_irq(struct acpi_pci_link_softc * sc,struct link * link)963 acpi_pci_link_choose_irq(struct acpi_pci_link_softc *sc, struct link *link)
964 {
965 u_int8_t best_irq, pos_irq;
966 int best_weight, pos_weight, i;
967
968 KASSERT(!link->l_routed);
969 KASSERT(!PCI_INTERRUPT_VALID(link->l_irq));
970
971 /*
972 * If we have a valid BIOS IRQ, use that. We trust what the BIOS
973 * says it routed over what _CRS says the link thinks is routed.
974 */
975 if (PCI_INTERRUPT_VALID(link->l_bios_irq))
976 return link->l_bios_irq;
977
978 /*
979 * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
980 * then use that.
981 */
982 if (PCI_INTERRUPT_VALID(link->l_initial_irq))
983 return link->l_initial_irq;
984
985 /*
986 * Ok, we have no useful hints, so we have to pick from the
987 * possible IRQs. For ISA IRQs we only use interrupts that
988 * have already been used by the BIOS.
989 */
990 best_irq = PCI_INVALID_IRQ;
991 best_weight = INT_MAX;
992 for (i = 0; i < link->l_num_irqs; i++) {
993 pos_irq = link->l_irqs[i];
994 if (pos_irq < NUM_ISA_INTERRUPTS &&
995 (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
996 continue;
997 pos_weight = pci_link_interrupt_weights[pos_irq];
998 if (pos_weight < best_weight) {
999 best_weight = pos_weight;
1000 best_irq = pos_irq;
1001 }
1002 }
1003
1004 /*
1005 * If this is an ISA IRQ, try using the SCI if it is also an ISA
1006 * interrupt as a fallback.
1007 */
1008 if (link->l_isa_irq && !PCI_INTERRUPT_VALID(best_irq)) {
1009 pos_irq = AcpiGbl_FADT.SciInterrupt;
1010 pos_weight = pci_link_interrupt_weights[pos_irq];
1011 if (pos_weight < best_weight) {
1012 best_weight = pos_weight;
1013 best_irq = pos_irq;
1014 }
1015 }
1016
1017 if (PCI_INTERRUPT_VALID(best_irq)) {
1018 aprint_verbose("%s: Picked IRQ %u with weight %d\n",
1019 sc->pl_name, best_irq, best_weight);
1020 } else
1021 printf("%s: Unable to choose an IRQ\n", sc->pl_name);
1022 return best_irq;
1023 }
1024
1025 int
acpi_pci_link_route_interrupt(void * v,pci_chipset_tag_t pc,int index,int * irq,int * pol,int * trig)1026 acpi_pci_link_route_interrupt(void *v, pci_chipset_tag_t pc, int index,
1027 int *irq, int *pol, int *trig)
1028 {
1029 struct acpi_pci_link_softc *sc = v;
1030 struct link *link;
1031 int i;
1032 pcireg_t reg;
1033
1034 ACPI_SERIAL_BEGIN(pci_link);
1035 link = acpi_pci_link_lookup(sc, index);
1036 if (link == NULL)
1037 panic("%s: apparently invalid index %d", __func__, index);
1038
1039 /*
1040 * If this link device is already routed to an interrupt, just return
1041 * the interrupt it is routed to.
1042 */
1043 if (link->l_routed) {
1044 KASSERT(PCI_INTERRUPT_VALID(link->l_irq));
1045 ACPI_SERIAL_END(pci_link);
1046 *irq = link->l_irq;
1047 *pol = link->l_pol;
1048 *trig = link->l_trig;
1049 return link->l_irq;
1050 }
1051
1052 if (PCI_INTERRUPT_VALID(link->l_irq)) {
1053 *irq = link->l_irq;
1054 *pol = link->l_pol;
1055 *trig = link->l_trig;
1056 goto done;
1057 }
1058
1059 /* The link device doesn't have an interrupt, so try to choose one. */
1060 link->l_irq = acpi_pci_link_choose_irq(sc, link);
1061 if (!PCI_INTERRUPT_VALID(link->l_irq))
1062 goto done;
1063
1064 /*
1065 * Try to route the interrupt we picked. If it fails, then
1066 * assume the interrupt is not routed.
1067 */
1068 acpi_pci_link_route_irqs(sc, irq, pol, trig);
1069 if (!link->l_routed) {
1070 link->l_irq = PCI_INVALID_IRQ;
1071 goto done;
1072 }
1073
1074 link->l_pol = *pol;
1075 link->l_trig = *trig;
1076 for (i = 0; i < link->l_dev_count; ++i) {
1077 reg = pci_conf_read(pc, link->l_devices[i],
1078 PCI_INTERRUPT_REG);
1079 reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
1080 reg |= link->l_irq << PCI_INTERRUPT_LINE_SHIFT;
1081 pci_conf_write(pc, link->l_devices[i],
1082 PCI_INTERRUPT_REG, reg);
1083 }
1084
1085 done:
1086 ACPI_SERIAL_END(pci_link);
1087
1088 return link->l_irq;
1089 }
1090
1091 /*
1092 * This is gross, but we abuse the identify routine to perform one-time
1093 * SYSINIT() style initialization for the driver.
1094 */
1095 static void
acpi_pci_link_init(struct acpi_pci_link_softc * sc)1096 acpi_pci_link_init(struct acpi_pci_link_softc *sc)
1097 {
1098 ACPI_BUFFER buf;
1099
1100 /*
1101 * If the SCI is an ISA IRQ, add it to the bitmask of known good
1102 * ISA IRQs.
1103 *
1104 * XXX: If we are using the APIC, the SCI might have been
1105 * rerouted to an APIC pin in which case this is invalid. However,
1106 * if we are using the APIC, we also shouldn't be having any PCI
1107 * interrupts routed via ISA IRQs, so this is probably ok.
1108 */
1109 if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS)
1110 pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
1111
1112 buf.Length = sizeof (sc->pl_name);
1113 buf.Pointer = sc->pl_name;
1114
1115 if (ACPI_FAILURE(AcpiGetName(sc->pl_handle, ACPI_SINGLE_NAME, &buf)))
1116 snprintf(sc->pl_name, sizeof (sc->pl_name), "%s",
1117 "ACPI link device");
1118
1119 acpi_pci_link_attach(sc);
1120 }
1121
1122 void *
acpi_pci_link_devbyhandle(ACPI_HANDLE handle)1123 acpi_pci_link_devbyhandle(ACPI_HANDLE handle)
1124 {
1125 struct acpi_pci_link_softc *sc;
1126
1127 TAILQ_FOREACH(sc, &acpi_pci_linkdevs, pl_list) {
1128 if (sc->pl_handle == handle)
1129 return sc;
1130 }
1131
1132 sc = malloc(sizeof (*sc), M_ACPI, M_WAITOK | M_ZERO);
1133 sc->pl_handle = handle;
1134
1135 acpi_pci_link_init(sc);
1136
1137 TAILQ_INSERT_TAIL(&acpi_pci_linkdevs, sc, pl_list);
1138
1139 return (void *)sc;
1140 }
1141
1142 void
acpi_pci_link_resume(void)1143 acpi_pci_link_resume(void)
1144 {
1145 struct acpi_pci_link_softc *sc;
1146 ACPI_BUFFER srsbuf;
1147
1148 TAILQ_FOREACH(sc, &acpi_pci_linkdevs, pl_list) {
1149 ACPI_SERIAL_BEGIN(pci_link);
1150 if (ACPI_SUCCESS(acpi_pci_link_srs(sc, &srsbuf)))
1151 ACPI_FREE(srsbuf.Pointer);
1152 ACPI_SERIAL_END(pci_link);
1153 }
1154 }
1155
1156 ACPI_HANDLE
acpi_pci_link_handle(void * v)1157 acpi_pci_link_handle(void *v)
1158 {
1159 struct acpi_pci_link_softc *sc = v;
1160
1161 return sc->pl_handle;
1162 }
1163
1164 char *
acpi_pci_link_name(void * v)1165 acpi_pci_link_name(void *v)
1166 {
1167 struct acpi_pci_link_softc *sc = v;
1168
1169 return sc->pl_name;
1170 }
1171
1172
1173 /*
1174 * Append an ACPI_RESOURCE to an ACPI_BUFFER.
1175 *
1176 * Given a pointer to an ACPI_RESOURCE structure, expand the ACPI_BUFFER
1177 * provided to contain it. If the ACPI_BUFFER is empty, allocate a sensible
1178 * backing block. If the ACPI_RESOURCE is NULL, return an empty set of
1179 * resources.
1180 */
1181 #define ACPI_INITIAL_RESOURCE_BUFFER_SIZE 512
1182
1183 static ACPI_STATUS
acpi_AppendBufferResource(ACPI_BUFFER * buf,ACPI_RESOURCE * res)1184 acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res)
1185 {
1186 ACPI_RESOURCE *rp;
1187 void *newp;
1188
1189 /* Initialise the buffer if necessary. */
1190 if (buf->Pointer == NULL) {
1191 buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE;
1192 if ((buf->Pointer = ACPI_ALLOCATE(buf->Length)) == NULL)
1193 return (AE_NO_MEMORY);
1194 rp = (ACPI_RESOURCE *)buf->Pointer;
1195 rp->Type = ACPI_RESOURCE_TYPE_END_TAG;
1196 rp->Length = 0;
1197 }
1198
1199 if (res == NULL)
1200 return AE_OK;
1201
1202 /*
1203 * Scan the current buffer looking for the terminator.
1204 * This will either find the terminator or hit the end
1205 * of the buffer and return an error.
1206 */
1207 rp = (ACPI_RESOURCE *)buf->Pointer;
1208 for (;;) {
1209 /* Range check, don't go outside the buffer */
1210 if (rp >= (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer +
1211 buf->Length))
1212 return AE_BAD_PARAMETER;
1213 if (rp->Type == ACPI_RESOURCE_TYPE_END_TAG || rp->Length == 0)
1214 break;
1215 rp = ACPI_NEXT_RESOURCE(rp);
1216 }
1217
1218 /*
1219 * Check the size of the buffer and expand if required.
1220 *
1221 * Required size is:
1222 * size of existing resources before terminator +
1223 * size of new resource and header +
1224 * size of terminator.
1225 *
1226 * Note that this loop should really only run once, unless
1227 * for some reason we are stuffing a *really* huge resource.
1228 */
1229 while ((((u_int8_t *)rp - (u_int8_t *)buf->Pointer) +
1230 res->Length + ACPI_RS_SIZE_NO_DATA +
1231 ACPI_RS_SIZE_MIN) >= buf->Length) {
1232 if ((newp = ACPI_ALLOCATE(buf->Length * 2)) == NULL)
1233 return AE_NO_MEMORY;
1234 memcpy(newp, buf->Pointer, buf->Length);
1235 rp = (ACPI_RESOURCE *)((u_int8_t *)newp +
1236 ((u_int8_t *)rp - (u_int8_t *)buf->Pointer));
1237 ACPI_FREE(buf->Pointer);
1238 buf->Pointer = newp;
1239 buf->Length += buf->Length;
1240 }
1241
1242 /* Insert the new resource. */
1243 memcpy(rp, res, res->Length);
1244
1245 /* And add the terminator. */
1246 rp = ACPI_NEXT_RESOURCE(rp);
1247 rp->Type = ACPI_RESOURCE_TYPE_END_TAG;
1248 rp->Length = 0;
1249
1250 return AE_OK;
1251 }
1252