eb0efefd8f1b211715a08d47e0f7acd25a079a45
[barrelfish] / usr / acpi / acpi.c
1 /**
2  * \file
3  * \brief ACPI management
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <stdio.h>
16 #include <barrelfish/barrelfish.h>
17 #include <barrelfish/nameservice_client.h>
18 #include <barrelfish_kpi/types.h>
19 #include <acpi.h>
20 #include <mm/mm.h>
21 #include <octopus/getset.h>
22 #include <octopus/barrier.h>
23 #include <skb/skb.h>
24 #include <pci/confspace/pci_confspace.h>
25 #include "acpi_shared.h"
26 #include "acpi_debug.h"
27 #include "ioapic.h"
28
29 struct pci_resources {
30     uint8_t minbus, maxbus;
31     lpaddr_t minmem, maxmem;
32     struct pci_address addr;
33 };
34
35 struct memrange {
36     lpaddr_t min;
37     lpaddr_t limit;
38 };
39
40 #define MAX_RESERVED_MEM_REGIONS 32
41 // Hack: reserved memory regions (eg. PCIe config space mapping)
42 static struct memrange reserved_memory[MAX_RESERVED_MEM_REGIONS];
43 static int n_reserved_memory_regions;
44
45 static ACPI_STATUS pci_resource_walker(ACPI_RESOURCE *resource, void *context)
46 {
47     struct pci_resources *ret = context;
48 //    uint64_t granularity, min, max, translationoffset, addrlength;
49     lpaddr_t granularity, min, max, translationoffset, addrlength;
50
51     switch (resource->Type) {
52     case ACPI_RESOURCE_TYPE_ADDRESS16:
53         granularity = resource->Data.Address16.Granularity;
54         min = resource->Data.Address16.Minimum;
55         max = resource->Data.Address16.Maximum;
56         translationoffset = resource->Data.Address16.TranslationOffset;
57         addrlength = resource->Data.Address16.AddressLength;
58         break;
59
60     case ACPI_RESOURCE_TYPE_ADDRESS32:
61         granularity = resource->Data.Address32.Granularity;
62         min = resource->Data.Address32.Minimum;
63         max = resource->Data.Address32.Maximum;
64         translationoffset = resource->Data.Address32.TranslationOffset;
65         addrlength = resource->Data.Address32.AddressLength;
66         break;
67
68     case ACPI_RESOURCE_TYPE_ADDRESS64:
69         granularity = resource->Data.Address64.Granularity;
70         min = resource->Data.Address64.Minimum;
71         max = resource->Data.Address64.Maximum;
72         translationoffset = resource->Data.Address64.TranslationOffset;
73         addrlength = resource->Data.Address32.AddressLength;
74         break;
75
76     default:
77         return AE_OK;
78     }
79
80     /* Ignore bogus entries with zero length */
81     if (addrlength == 0) {
82         ACPI_DEBUG("Warning: ignoring zero-length address resource\n");
83         return AE_OK;
84     }
85
86     /* TODO: handle non-fixed regions. Does anything other than QEMU do this? */
87     if (resource->Data.Address.MinAddressFixed != ACPI_ADDRESS_FIXED ||
88         resource->Data.Address.MaxAddressFixed != ACPI_ADDRESS_FIXED) {
89         ACPI_DEBUG("Warning: Treating non-fixed address range resource as fixed\n");
90     }
91
92     switch (resource->Data.Address.ResourceType) {
93     case ACPI_BUS_NUMBER_RANGE:
94         assert(max > 0);
95         assert(ret->maxbus == 0); /* shouldn't have more than one of these */
96         ret->minbus = min;
97         ret->maxbus = max;
98         break;
99
100     case ACPI_MEMORY_RANGE:
101         ACPI_DEBUG("PCI mem range %lx-%lx granularity 0x%lx translation offset "
102                   " 0x%lx length 0x%lx prodcons %u decode %u writeprot %u"
103                   " caching %u rangetype %u translation %u\n", min, max,
104                   granularity, translationoffset, addrlength,
105                   resource->Data.Address.ProducerConsumer,
106                   resource->Data.Address.Decode,
107                   resource->Data.Address.Info.Mem.WriteProtect,
108                   resource->Data.Address.Info.Mem.Caching,
109                   resource->Data.Address.Info.Mem.RangeType,
110                   resource->Data.Address.Info.Mem.Translation);
111 /*
112         // check for overlaps with reserved memory regions
113         for (int i = 0; i < n_reserved_memory_regions; i++) {
114             struct memrange *range = &reserved_memory[i];
115             if (min < range->limit && max >= range->min) {
116                 if (min < range->limit && min >= range->min) {
117                     // overlaps with min: take top part
118                     min = range->limit;
119                 } else if (max - range->limit > range->min - min) {
120                     // take top part
121                     min = range->limit;
122                 } else {
123                     // take bottom part
124                     max = range->min - 1;
125                 }
126                 if (min > max) {
127                     min = max = 0;
128                 }
129                 ACPI_DEBUG("mem range overlaps reserved space [%lx,%lx], truncated"
130                           " to %lx-%lx\n", range->min, range->limit, min, max);
131             }
132         }
133 */
134         skb_add_fact("rootbridge_address_window(addr(%u, %u, %u), mem(%"PRIuLPADDR", %"PRIuLPADDR")).",
135             ret->addr.bus, ret->addr.device, ret->addr.function,
136             min, max);
137         if (ret->minmem == ret->maxmem) {
138             /* this is the first region we've seen */
139             ret->minmem = min;
140             ret->maxmem = max;
141         } else if (min == ret->maxmem + 1) {
142             /* this region extends the existing region */
143             ret->maxmem = max;
144         } else if (max - min > ret->maxmem - ret->minmem) {
145             /* this region is bigger than the existing region */
146             ret->minmem = min;
147             ret->maxmem = max;
148         }
149         break;
150     }
151
152     return AE_OK;
153 }
154
155 #ifdef PCI_SERVICE_DEBUG
156 static ACPI_STATUS resource_printer(ACPI_RESOURCE *res, void *context)
157 {
158     switch(res->Type) {
159     case ACPI_RESOURCE_TYPE_END_TAG:
160         return AE_OK;
161
162     case ACPI_RESOURCE_TYPE_ADDRESS16:
163         printf("addr16\n");
164         break;
165     case ACPI_RESOURCE_TYPE_ADDRESS32:
166         printf("addr32\n");
167         break;
168     case ACPI_RESOURCE_TYPE_ADDRESS64:
169         printf("length = %"PRIu32", gran = %lx, min = %lx, max = %lx, transoff "
170                "= %lx, addrlen = %lx, index = %hhu, strlen = %hu, string = %s",
171                res->Length, res->Data.Address64.Granularity, 
172                res->Data.Address64.Minimum,
173                res->Data.Address64.Maximum,
174                res->Data.Address64.TranslationOffset,
175                res->Data.Address64.AddressLength,
176                res->Data.Address64.ResourceSource.Index,
177                res->Data.Address64.ResourceSource.StringLength,
178                res->Data.Address64.ResourceSource.StringPtr
179                );
180         break;
181
182     case ACPI_RESOURCE_TYPE_IRQ:
183         {
184             ACPI_RESOURCE_IRQ *irqres = &res->Data.Irq;
185
186             printf("%s, %s triggered, active %s, ",
187                    irqres->Sharable ? "shared" : "exclusive",
188                    irqres->Triggering ? "edge" : "level",
189                    irqres->Polarity ? "low" : "high");
190
191             if (irqres->InterruptCount > 0) {
192                 printf("IRQs:");
193                 for (int i = 0; i < irqres->InterruptCount; i++) {
194                     printf(" %d", irqres->Interrupts[i]);
195                 }
196                 printf(".\n");
197             } else {
198                 printf("no IRQ.\n");
199             }
200         }
201         break;
202
203     case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
204         {
205             ACPI_RESOURCE_EXTENDED_IRQ *irqres = &res->Data.ExtendedIrq;
206
207             printf("%s, %s triggered, active %s, ",
208                    irqres->Sharable ? "shared" : "exclusive",
209                    irqres->Triggering ? "edge" : "level",
210                    irqres->Polarity ? "low" : "high");
211
212             if (irqres->InterruptCount > 0) {
213                 printf("IRQs:");
214                 for (int i = 0; i < irqres->InterruptCount; i++) {
215                     printf(" %d", irqres->Interrupts[i]);
216                 }
217             } else {
218                 printf("no IRQ");
219             }
220
221             ACPI_RESOURCE_SOURCE *src = &irqres->ResourceSource;
222
223             if(src->StringLength > 0) {
224                 printf(", resource index %d, source %.*s\n", src->Index,
225                        src->StringLength, src->StringPtr);
226             } else {
227                 printf(".\n");
228             }
229         }
230         break;
231
232     default:
233         printf("resource_printer: Unexpected resource type %d\n", res->Type);
234         break;
235     }
236
237     return AE_OK;
238 }
239 #endif
240
241 ACPI_STATUS acpi_eval_integer(ACPI_HANDLE handle, char *name, ACPI_INTEGER *ret)
242 {
243     assert(ret != NULL);
244     ACPI_STATUS as;
245     char intbuf[sizeof(ACPI_OBJECT)];
246     ACPI_BUFFER intbufobj = {.Length = sizeof(intbuf), .Pointer = intbuf};
247
248     as = AcpiEvaluateObjectTyped(handle, name, NULL, &intbufobj, ACPI_TYPE_INTEGER);
249     if (ACPI_SUCCESS(as)) {
250         ACPI_OBJECT *obj = intbufobj.Pointer;
251         *ret = obj->Integer.Value;
252     }
253
254     return as;
255 }
256
257 static ACPI_STATUS fixed_resource_walker(ACPI_RESOURCE *resource, void *context)
258 {
259     if (resource->Type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
260         struct memrange range = {
261             .min = resource->Data.FixedMemory32.Address,
262             .limit = resource->Data.FixedMemory32.Address
263                 + resource->Data.FixedMemory32.AddressLength
264         };
265         ACPI_DEBUG("fixed memory resource claimed: 0x%"PRIxLPADDR"-%"PRIxLPADDR"\n",
266                   range.min, range.limit);
267
268         /* XXX: TODO: insert something in the SKB */
269         assert(n_reserved_memory_regions < MAX_RESERVED_MEM_REGIONS);
270         reserved_memory[n_reserved_memory_regions++] = range;
271         skb_add_fact("fixed_memory(%"PRIuLPADDR",%"PRIuLPADDR").", range.min,
272             range.limit);
273     }
274
275     return AE_OK;
276 }
277
278 static ACPI_STATUS reserve_resources(ACPI_HANDLE handle, UINT32 level,
279                                      void *context, void **retval)
280 {
281     ACPI_STATUS as;
282
283     /* walk _CRS resources looking for fixed resources */
284     as = AcpiWalkResources(handle, METHOD_NAME__CRS, fixed_resource_walker, NULL);
285     if (ACPI_FAILURE(as)) {
286         return as;
287     }
288
289     return AE_OK;
290 }
291
292 /**
293  * \brief Get IRQ routing table by querying _PRT method.
294  *
295  * \param handle        Handle to _PRT method.
296  * \param bus           Bus number this _PRT method is for.
297  */
298 static void get_irq_routing(ACPI_HANDLE handle, uint8_t bus)
299 {
300     ACPI_STATUS as;
301     char prtbuf[2048];
302     ACPI_BUFFER bufobj = {.Length = sizeof(prtbuf), .Pointer = prtbuf};
303
304     /* do we have an interrupt routing table? */
305     as = AcpiGetIrqRoutingTable(handle, &bufobj);
306     if (ACPI_FAILURE(as)) {
307         ACPI_DEBUG("No IRQ routing table found: %s\n", AcpiFormatException(as));
308         return;
309     }
310
311     //printf("PCI IRQ routing table:\n");
312     ACPI_PCI_ROUTING_TABLE *prt = bufobj.Pointer;
313     for (; prt->Length; prt = (void *)prt + prt->Length) {
314         uint16_t device = (prt->Address >> 16) & 0xffff;
315         assert((prt->Address & 0xffff) == 0xffff); // any function
316         ACPI_DEBUG(" device %u pin %u %s (index %u)\n",
317                device, prt->Pin, *(prt->Source) ? prt->Source : "GSI",
318                prt->SourceIndex);
319
320         if (*prt->Source == 0) {
321             /* this is a global interrupt number */
322             skb_add_fact("prt(addr(%"PRIu8", %"PRIu16", _), %"PRIu32", gsi(%"PRIu32")).",
323                          bus, device, prt->Pin, prt->SourceIndex);
324             continue;
325         }
326
327         ACPI_HANDLE source;
328         as = AcpiGetHandle(handle, prt->Source, &source);
329         if (ACPI_FAILURE(as)) {
330             ACPI_DEBUG("  failed lookup: %s\n", AcpiFormatException(as));
331             continue;
332         }
333
334         assert(device < PCI_NDEVICES);
335         assert(prt->Pin >= 0 && prt->Pin < PCI_NINTPINS);
336
337         char *esource = calloc(strlen(prt->Source) * 2, 1);
338         for(int i = 0, j = 0; i < strlen(prt->Source) + 1; i++, j++) {
339             esource[j] = prt->Source[i];
340             if(prt->Source[i] == '\\') {
341                 esource[++j] = '\\';
342             }
343         }
344         skb_add_fact("prt(addr(%"PRIu8", %"PRIu16", _), %"PRIu32", pir(\"%s\")).",
345                      bus, device, prt->Pin, esource);
346
347 #ifdef PCI_SERVICE_DEBUG /* debug code to dump resources */
348         ACPI_DEBUG("  INITIAL:  ");
349         as = AcpiWalkResources(source, METHOD_NAME__CRS,
350                                resource_printer, NULL);
351         if (ACPI_FAILURE(as)) {
352             ACPI_DEBUG("  failed walking _CRS: %s\n", AcpiFormatException(as));
353         }
354
355         ACPI_DEBUG("  POSSIBLE: ");
356         as = AcpiWalkResources(source, METHOD_NAME__PRS,
357                                resource_printer, NULL);
358         if (ACPI_FAILURE(as)) {
359             ACPI_DEBUG("  failed walking _PRS: %s\n", AcpiFormatException(as));
360         }
361 #endif
362
363         uint8_t data[512];
364         ACPI_BUFFER buf = { .Length = sizeof(data), .Pointer = &data };
365         as = AcpiGetPossibleResources(source, &buf);
366         if (ACPI_FAILURE(as)) {
367             ACPI_DEBUG("  failed retrieving _PRS: %s\n",
368                       AcpiFormatException(as));
369             free(esource);
370             continue;
371         }
372
373         for(ACPI_RESOURCE *res = buf.Pointer;
374             (void *)res < buf.Pointer + buf.Length;
375             res = (ACPI_RESOURCE *)(((char *)res) + res->Length)) {
376
377             if(res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
378                 break;
379             }
380
381             switch(res->Type) {
382             case ACPI_RESOURCE_TYPE_IRQ:
383             {
384                 ACPI_RESOURCE_IRQ *irqres = &res->Data.Irq;
385                 //printf("IRQs:");
386                 for (int i = 0; i < irqres->InterruptCount; i++) {
387                     skb_add_fact("pir(\"%s\", %u).",
388                                  esource, irqres->Interrupts[i]);
389                     //printf(" %d", irqres->Interrupts[i]);
390                 }
391                 //printf("\n");
392                 break;
393             }
394
395             case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
396             {
397                 ACPI_RESOURCE_EXTENDED_IRQ *irqres = &res->Data.ExtendedIrq;
398                 //printf("Extended IRQs:");
399                 for (int i = 0; i < irqres->InterruptCount; i++) {
400                     //printf(" %d", irqres->Interrupts[i]);
401                     skb_add_fact("pir(\"%s\", %"PRIu32").",
402                                  esource, irqres->Interrupts[i]);
403                 }
404                 //printf("\n");
405                 break;
406             }
407
408             default:
409                 printf("Unknown resource type: %"PRIu32"\n", res->Type);
410                 USER_PANIC("NYI");
411                 break;
412             }
413         }
414
415         free(esource);
416     }
417 }
418
419 void acpi_get_irqtable_device(ACPI_HANDLE parent,
420         acpi_pci_address_t device, ACPI_HANDLE *child, uint8_t bus)
421 {
422 /*     char b[128]; */
423 /*     ACPI_BUFFER buf = { .Length = 128, .Pointer = b }; */
424 /*     ACPI_STATUS s; */
425
426     *child = NULL;
427
428     if(parent == NULL) {
429         return;
430     }
431
432 /*     s = AcpiGetName(parent, ACPI_FULL_PATHNAME, &buf); */
433 /*     assert(ACPI_SUCCESS(s)); */
434 /*     printf("Parent: %s\n", b); */
435
436     for(;;) {
437         ACPI_STATUS as =
438             AcpiGetNextObject(ACPI_TYPE_DEVICE, parent, *child, child);
439
440         if(as == AE_NOT_FOUND || *child == NULL) {
441             return;
442         }
443
444         if(ACPI_FAILURE(as)) {
445             ACPI_DEBUG("Error looking up ACPI children\n");
446             abort();
447         }
448
449 /*         s = AcpiGetName(*child, ACPI_FULL_PATHNAME, &buf); */
450 /*         if(ACPI_FAILURE(s)) { */
451 /*             printf("Name lookup failure: %d\n", s); */
452 /*         } else { */
453 /*             printf("Current: %s\n", b); */
454 /*         } */
455
456         /* look for a _ADR node, which tells us the bridge's configuration space */
457         ACPI_INTEGER addr;
458         as = acpi_eval_integer(*child, "_ADR", &addr);
459         if (ACPI_FAILURE(as)) {
460             continue;
461         }
462
463         acpi_pci_address_t bridgeaddr;
464         bridgeaddr.bus = 0;
465         bridgeaddr.device = (addr >> 16) & 0xffff;
466         bridgeaddr.function = addr & 0xffff;
467
468         if(device.device == bridgeaddr.device
469            && device.function == bridgeaddr.function) {
470 /*             printf("Found corresponding ACPI bridge device!\n"); */
471             get_irq_routing(*child, bus);
472         }
473     }
474 }
475
476 static ACPI_STATUS add_pci_device(ACPI_HANDLE handle, UINT32 level,
477                                   void *context, void **retval)
478 {
479     ACPI_STATUS as;
480     char namebuf[128];
481     ACPI_BUFFER bufobj = {.Length = sizeof(namebuf), .Pointer = namebuf};
482
483     /* get the node's name */
484     as = AcpiGetName(handle, ACPI_FULL_PATHNAME, &bufobj);
485     if (ACPI_FAILURE(as)) {
486         return as;
487     }
488     assert(bufobj.Pointer == namebuf);
489
490     ACPI_HANDLE handle2;
491     as = AcpiGetHandle(NULL, namebuf, &handle2);
492     ACPI_DEBUG("acpi get handle for %s\n", namebuf);
493     assert(ACPI_SUCCESS(as) && handle == handle2);
494
495
496     /* look for a _ADR node, which tells us the bridge's configuration space */
497     ACPI_INTEGER addr;
498     as = acpi_eval_integer(handle, "_ADR", &addr);
499     if (ACPI_FAILURE(as)) {
500         return as;
501     }
502
503     struct pci_address bridgeaddr;
504     bridgeaddr.bus = 0;
505     bridgeaddr.device = (addr >> 16) & 0xffff;
506     bridgeaddr.function = addr & 0xffff;
507
508     /* look for a _BBN node, which tells us the bus number on a multi-root box */
509     ACPI_INTEGER busnum;
510     as = acpi_eval_integer(handle, "_BBN", &busnum);
511     if (ACPI_SUCCESS(as)) {
512         bridgeaddr.bus = busnum;
513     }
514
515     /* walk resources looking for the child bus ranges */
516     struct pci_resources resources;
517     memset(&resources, 0, sizeof(resources));
518
519     resources.addr = bridgeaddr;
520
521 #ifdef PCI_SERVICE_DEBUG
522     printf("\nstart PRS\n");
523     as = AcpiWalkResources(handle, METHOD_NAME__PRS, resource_printer,
524                            NULL);
525     printf("\nPRS finished\n");
526     if (ACPI_FAILURE(as)) {
527         printf("\nPRS failed. Status = %d\n", as);
528 //        return as;
529     }
530 #endif
531     as = AcpiWalkResources(handle, METHOD_NAME__CRS, pci_resource_walker,
532                            &resources);
533     if (ACPI_FAILURE(as)) {
534         return as;
535     }
536
537     if (resources.maxbus == 0) {
538         ACPI_DEBUG("%s: invalid PCI root at %u:%u:%u? Ignored.\n",
539                namebuf, bridgeaddr.bus, bridgeaddr.device, bridgeaddr.function);
540         return AE_OK;
541     }
542
543     get_irq_routing(handle, bridgeaddr.bus);
544
545     ACPI_DEBUG("%s: root at %u:%u:%u child buses %u-%u memory 0x%lx-%lx\n",
546            namebuf, bridgeaddr.bus, bridgeaddr.device, bridgeaddr.function,
547            resources.minbus, resources.maxbus, resources.minmem,
548            resources.maxmem + 1);
549
550     skb_add_fact("rootbridge(addr(%u,%u,%u),childbus(%u,%u),mem(%" PRIuPTR ",%" PRIuPTR ")).",
551            bridgeaddr.bus, bridgeaddr.device, bridgeaddr.function,
552            resources.minbus, resources.maxbus, resources.minmem,
553            resources.maxmem);
554
555     // octopus record for rootbridge
556     ACPI_DEBUG("acpi_node: %s\n", namebuf);
557     static char* format = "hw.pci.rootbridge. { bus: %lu, device: %lu, function: %lu, maxbus: %lu, acpi_node: '%s' }";
558     errval_t err = oct_mset(SET_SEQUENTIAL, format,
559             bridgeaddr.bus, bridgeaddr.device, bridgeaddr.function,
560             resources.maxbus, namebuf);
561     assert(err_is_ok(err));
562     // end
563
564     // XXX: enable PCIe for bridge programming
565     /*
566     pcie_enable();
567     pci_add_root(bridgeaddr, resources.maxbus, handle);
568     pcie_disable();*/
569
570     return AE_OK;
571 }
572
573 static int acpi_init(void)
574 {
575     AcpiDbgLevel = 0; // ACPI_DEBUG_DEFAULT | ACPI_LV_INFO | ACPI_LV_EXEC;
576
577     // enable workarounds for non-compliant ACPI bytecode
578     AcpiGbl_EnableInterpreterSlack = TRUE;
579
580     ACPI_STATUS as;
581     as = AcpiInitializeSubsystem();
582     if (ACPI_FAILURE(as)) {
583         ACPI_DEBUG("AcpiInitializeSubsystem failed\n");
584         return -1;
585     }
586
587     as = AcpiInitializeTables(NULL, 0, false);
588     if (ACPI_FAILURE(as)) {
589         ACPI_DEBUG("AcpiInitializeTables failed\n");
590         return -1;
591     }
592
593     as = AcpiLoadTables();
594     if (ACPI_FAILURE(as)) {
595         ACPI_DEBUG("AcpiLoadTables failed %s\n", AcpiFormatException(as));
596         return -1;
597     }
598
599     ACPI_DEBUG("Scanning local and I/O APICs...\n");
600     int r = init_all_apics();
601     assert(r == 0);
602
603 #ifdef USE_KALUGA_DVM
604     char* record;
605     errval_t err = oct_barrier_enter("barrier.acpi", &record, 2);
606     assert(err_is_ok(err));
607 #endif
608
609     as = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION);
610     if (ACPI_FAILURE(as)) {
611         ACPI_DEBUG("AcpiEnableSubsystem failed %s\n", AcpiFormatException(as));
612         return -1;
613     }
614
615     // find and init any embedded controller drivers
616     // we do this early, because control methods may need to access the EC space
617     ec_probe_ecdt();
618
619     as = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION);
620     if (ACPI_FAILURE(as)) {
621         ACPI_DEBUG("AcpiInitializeObjects failed\n");
622         return -1;
623     }
624
625     return 0;
626 }
627
628 /**
629  * \brief Sets system into APIC mode.
630  *
631  * Evaluates _PIC method to argument 1 and disables dual-8259As.
632  * This changes the IRQs reported/set with the interrupt routing (PRT) methods.
633  */
634 static ACPI_STATUS set_apic_mode(void)
635 {
636     ACPI_OBJECT arg1;
637     ACPI_OBJECT_LIST args;
638     ACPI_STATUS as;
639
640     // Evaluate _PIC method to argument 1
641     arg1.Type = ACPI_TYPE_INTEGER;
642     arg1.Integer.Value = 1;
643     args.Count = 1;
644     args.Pointer = &arg1;
645
646     as = AcpiEvaluateObject(ACPI_ROOT_OBJECT, "_PIC", &args, NULL);
647
648     // Bail out if this didn't work
649     if(ACPI_FAILURE(as)) {
650         return as;
651     }
652
653     return AE_OK;
654 }
655
656 static void process_srat(ACPI_TABLE_SRAT *srat)
657 {
658     assert(!strncmp(srat->Header.Signature, "SRAT", ACPI_NAME_SIZE));
659     assert(srat->TableRevision == 1);
660
661     void *pos = (void *)srat + sizeof(ACPI_TABLE_SRAT);
662
663     // Scan subtables
664     while(pos < (void *)srat + srat->Header.Length) {
665         ACPI_SUBTABLE_HEADER *shead = pos;
666
667         switch(shead->Type) {
668         case ACPI_SRAT_TYPE_CPU_AFFINITY:
669             {
670                 ACPI_SRAT_CPU_AFFINITY *a = (ACPI_SRAT_CPU_AFFINITY *)shead;
671
672                 assert(a->Header.Length == 16);
673
674                 if(a->Flags & ACPI_SRAT_MEM_ENABLED) {
675                     uint32_t proximitydomain = (a->ProximityDomainHi[0] << 24) +
676                         (a->ProximityDomainHi[1] << 16) +
677                         (a->ProximityDomainHi[2] << 8) +
678                         a->ProximityDomainLo;
679
680                     ACPI_DEBUG("CPU affinity table:\n");
681                     ACPI_DEBUG("Proximity Domain: %"PRIu32"\n", proximitydomain);
682                     ACPI_DEBUG("CPU local APIC ID: %d\n", a->ApicId);
683                     ACPI_DEBUG("CPU local SAPIC EID: %d\n", a->LocalSapicEid);
684
685                     skb_add_fact("cpu_affinity(%d,%d,%"PRIu32").",
686                         a->ApicId, a->LocalSapicEid, proximitydomain);
687                 } else {
688                     ACPI_DEBUG("CPU affinity table disabled!\n");
689                 }
690
691                 pos += sizeof(ACPI_SRAT_CPU_AFFINITY);
692             }
693             break;
694
695         case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
696             {
697                 ACPI_SRAT_MEM_AFFINITY *a = (ACPI_SRAT_MEM_AFFINITY *)shead;
698
699                 assert(a->Header.Length == 40);
700
701                 if(a->Flags & ACPI_SRAT_MEM_ENABLED) {
702                     ACPI_DEBUG("Memory affinity table:\n");
703                     ACPI_DEBUG("Proximity Domain: %d\n", a->ProximityDomain);
704                     ACPI_DEBUG("Base address: 0x%lx\n", a->BaseAddress);
705                     ACPI_DEBUG("Length: 0x%lx\n", a->Length);
706
707                     bool hotpluggable = false, nonvolatile = false;
708                     if(a->Flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) {
709                         hotpluggable = true;
710                     }
711                     if(a->Flags & ACPI_SRAT_MEM_NON_VOLATILE) {
712                         nonvolatile = true;
713                     }
714                     ACPI_DEBUG("Flags:%s%s\n",
715                               hotpluggable ? " Hot-pluggable" : "",
716                               nonvolatile ? " Non-volatile" : "");
717
718                     skb_add_fact("memory_affinity(%" PRIu64 ", %" PRIu64 ", %"PRIu32").",
719                         a->BaseAddress, a->Length, a->ProximityDomain);
720
721                 } else {
722                     ACPI_DEBUG("Memory affinity table disabled!\n");
723                 }
724
725                 pos += sizeof(ACPI_SRAT_MEM_AFFINITY);
726             }
727             break;
728
729         case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
730             ACPI_DEBUG("Ignoring unsupported x2APIC CPU affinity table.\n");
731             break;
732
733         default:
734             ACPI_DEBUG("Ignoring unknown SRAT subtable ID %d.\n", shead->Type);
735             break;
736         }
737     }
738 }
739
740 int init_acpi(void)
741 {
742     ACPI_STATUS as;
743     int r;
744
745     ACPI_DEBUG("Initialising ACPI...\n");
746     r = acpi_init();
747     assert(r == 0);
748
749     // Put system into APIC mode
750     ACPI_DEBUG("Switching to APIC mode...\n");
751     as = set_apic_mode();
752     if(ACPI_FAILURE(as)) {
753         ACPI_DEBUG("Warning: Could not set system to APIC mode! "
754                   "Continuing anyway...\n");
755     }
756
757     /* look for an MCFG table
758      * this tells us where the PCI express memory-mapped configuration area is
759      */
760
761     /*
762     ACPI_TABLE_HEADER *mcfg_header;
763     as = AcpiGetTable("MCFG", 1, &mcfg_header);
764     if (ACPI_SUCCESS(as) && mcfg_header->Length >=
765             sizeof(ACPI_TABLE_MCFG) + sizeof(ACPI_MCFG_ALLOCATION)) {
766         ACPI_MCFG_ALLOCATION *mcfg = (void *)mcfg_header + sizeof(ACPI_TABLE_MCFG);
767         ACPI_DEBUG("PCIe enhanced configuration region at 0x%lx "
768                    "(segment %u, buses %u-%u)\n", mcfg->Address,
769                    mcfg->PciSegment, mcfg->StartBusNumber, mcfg->EndBusNumber);
770
771         skb_add_fact("pcie_confspace(%"PRIu64", %"PRIu16", %"PRIu8", %"PRIu8").",
772                 mcfg->Address, mcfg->PciSegment, mcfg->StartBusNumber,
773                 mcfg->EndBusNumber);
774
775         //XXX: Not needed as long as PCIe walking is disabled
776         r = pcie_confspace_init(mcfg->Address, mcfg->PciSegment,
777                                 mcfg->StartBusNumber, mcfg->EndBusNumber);
778         if (r == 0) {
779             // XXX: compute physical address region used by conf space
780             struct memrange confspace = {
781                 .min = mcfg->Address,
782                 .limit = mcfg->Address +
783                ((lpaddr_t)(mcfg->EndBusNumber + 1 - mcfg->StartBusNumber) << 20)
784             };
785             reserved_memory[n_reserved_memory_regions++] = confspace;
786         }
787
788     } else {
789         ACPI_DEBUG("No MCFG table found -> no PCIe enhanced configuration\n");
790     }*/
791
792     // XXX: disable PCIe memory-mapped config space until after we walk and
793     // prescan all the buses. This is necessary on some AMD boxes, because the
794     // ACPI table includes code to read registers in the HyperTransport config,
795     // and this only appears in IO space.
796     // We need a cleaner way of determining when to use PCIe config space!
797     pcie_disable();
798
799     /* Find and reserve all memory regions claimed by non-PCI devices */
800     ACPI_DEBUG("Reserving fixed resources\n");
801     as = AcpiGetDevices("PNP0C02", reserve_resources, NULL, NULL);
802     if (ACPI_FAILURE(as) && as != AE_NOT_FOUND) {
803         printf("WARNING: AcpiGetDevices failed with error %"PRIu32"\n", as);
804     }
805     assert(ACPI_SUCCESS(as) || as == AE_NOT_FOUND);
806
807     // XXX: PCIe walking disabled, as these also show up as PCI buses,
808     // and we don't currently distinguish between them
809     //ACPI_DEBUG("Walking for PCIe buses\n");
810     //as = AcpiGetDevices(PCI_EXPRESS_ROOT_HID_STRING, add_pci_device, NULL, NULL);
811     //assert(ACPI_SUCCESS(as));
812
813     ACPI_DEBUG("Walking for PCI buses\n");
814     as = AcpiGetDevices(PCI_ROOT_HID_STRING, add_pci_device, NULL, NULL);
815     assert(ACPI_SUCCESS(as));
816
817     //ACPI_DEBUG("Programming PCI BARs and bridge windows\n");
818     //pci_program_bridges();
819     //ACPI_DEBUG("PCI programming completed\n");
820
821     ACPI_TABLE_HEADER *srat_header;
822     as = AcpiGetTable("SRAT", 1, &srat_header);
823     if(ACPI_SUCCESS(as)) {
824         process_srat((ACPI_TABLE_SRAT *)srat_header);
825     }
826
827     return 0;
828 }