1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 
3 #include <stdio.h>
4 #include <stdarg.h>
5 #include <stdint.h>
6 #include <string.h>
7 #include <getopt.h>
8 #include <stdlib.h>
9 #include <stdbool.h>
10 #if defined(CONFIG_X86)
11 #include <xen/arch-x86/guest-acpi.h>
12 #include <xen/hvm/hvm_info_table.h>
13 #elif defined(CONFIG_ARM_64)
14 #include <xen/arch-arm.h>
15 #endif
16 
17 static unsigned int indent_level;
18 static bool debug = false;
19 
20 typedef enum dm_version {
21     QEMU_NONE,
22     QEMU_XEN_TRADITIONAL,
23     QEMU_XEN,
24 } dm_version;
25 
indent(void)26 static void indent(void)
27 {
28     unsigned int i;
29     for ( i = 0; i < indent_level; i++ )
30         printf("    ");
31 }
32 
33 static __attribute__((format(printf, 2, 3)))
_stmt(const char * name,const char * fmt,...)34 void _stmt(const char *name, const char *fmt, ...)
35 {
36     va_list args;
37 
38     indent();
39     printf("%s", name);
40 
41     if ( !fmt )
42         return;
43 
44     printf(" ( ");
45     va_start(args, fmt);
46     vprintf(fmt, args);
47     va_end(args);
48     printf(" )");
49 }
50 
51 #define stmt(n, f, a...)                        \
52     do {                                        \
53         _stmt(n, f , ## a );                    \
54         printf("\n");                           \
55     } while (0)
56 
57 #define push_block(n, f, a...)                  \
58     do {                                        \
59         _stmt(n, f , ## a );                    \
60         printf(" {\n");                         \
61         indent_level++;                         \
62     } while (0)
63 
pop_block(void)64 static void pop_block(void)
65 {
66     indent_level--;
67     indent();
68     printf("}\n");
69 }
70 
71 #ifdef CONFIG_X86
pci_hotplug_notify(unsigned int slt)72 static void pci_hotplug_notify(unsigned int slt)
73 {
74     stmt("Notify", "\\_SB.PCI0.S%02X, EVT", slt);
75 }
76 
decision_tree(unsigned int s,unsigned int e,char * var,void (* leaf)(unsigned int))77 static void decision_tree(
78     unsigned int s, unsigned int e, char *var, void (*leaf)(unsigned int))
79 {
80     if ( s == (e-1) )
81     {
82         (*leaf)(s);
83         return;
84     }
85 
86     push_block("If", "And(%s, 0x%02x)", var, (e-s)/2);
87     decision_tree((s+e)/2, e, var, leaf);
88     pop_block();
89     push_block("Else", NULL);
90     decision_tree(s, (s+e)/2, var, leaf);
91     pop_block();
92 }
93 #endif
94 
95 static struct option options[] = {
96     { "maxcpu", 1, 0, 'c' },
97 #ifdef CONFIG_X86
98     { "dm-version", 1, 0, 'q' },
99 #endif
100     { "debug", 1, 0, 'd' },
101     { 0, 0, 0, 0 }
102 };
103 
main(int argc,char ** argv)104 int main(int argc, char **argv)
105 {
106     unsigned int cpu, max_cpus;
107 #if defined(CONFIG_X86)
108     dm_version dm_version = QEMU_XEN_TRADITIONAL;
109     unsigned int slot, dev, intx, link;
110 
111     max_cpus = HVM_MAX_VCPUS;
112 #elif defined(CONFIG_ARM_64)
113     max_cpus = GUEST_MAX_VCPUS;
114 #endif
115 
116     for ( ; ; )
117     {
118         int opt = getopt_long(argc, argv, "", options, NULL);
119         if ( opt == -1 )
120             break;
121 
122         switch ( opt )
123         {
124         case 'c': {
125             long i = 0;
126             char *endptr;
127 
128             i = strtol(optarg, &endptr, 10);
129             if ( (*optarg != '\0') && (*endptr == '\0') && (i >= 0) )
130             {
131                 max_cpus = i;
132             }
133             else if ( !(strcmp(optarg, "any") == 0) )
134             {
135                 fprintf(stderr, "`%s' is not a number or is < 0.\n", optarg);
136                 return -1;
137             }
138             break;
139         }
140 #ifdef CONFIG_X86
141         case 'q':
142             if (strcmp(optarg, "qemu-xen") == 0) {
143                 dm_version = QEMU_XEN;
144             } else if (strcmp(optarg, "qemu-xen-traditional") == 0) {
145                 dm_version = QEMU_XEN_TRADITIONAL;
146             } else if (strcmp(optarg, "none") == 0) {
147                 dm_version = QEMU_NONE;
148             } else {
149                 fprintf(stderr, "Unknown device model version `%s'.\n", optarg);
150                 return -1;
151             }
152             break;
153 #endif
154         case 'd':
155             if (*optarg == 'y')
156                 debug = true;
157             break;
158         default:
159             return -1;
160         }
161     }
162 
163     /**** DSDT DefinitionBlock start ****/
164     /* (we append to existing DSDT definition block) */
165     indent_level++;
166 
167     /**** Processor start ****/
168     push_block("Scope", "\\_SB");
169 
170 #ifdef CONFIG_X86
171     /* MADT checksum */
172     stmt("OperationRegion", "MSUM, SystemMemory, \\_SB.MSUA, 1");
173     push_block("Field", "MSUM, ByteAcc, NoLock, Preserve");
174     indent(); printf("MSU, 8\n");
175     pop_block();
176 
177     /* Processor object helpers. */
178     push_block("Method", "PMAT, 2");
179     push_block("If", "LLess(Arg0, NCPU)");
180     stmt("Return", "ToBuffer(Arg1)");
181     pop_block();
182     stmt("Return", "Buffer() {0, 8, 0xff, 0xff, 0, 0, 0, 0}");
183     pop_block();
184 #endif
185 
186     /* Define processor objects and control methods. */
187     for ( cpu = 0; cpu < max_cpus; cpu++)
188     {
189         push_block("Processor", "PR%02X, %d, 0x0000b010, 0x06", cpu, cpu);
190 
191         stmt("Name", "_HID, \"ACPI0007\"");
192 
193         stmt("Name", "_UID, %d", cpu);
194 #ifdef CONFIG_ARM_64
195         pop_block();
196         continue;
197 #endif
198         /* Name this processor's MADT LAPIC descriptor. */
199         stmt("OperationRegion",
200              "MATR, SystemMemory, Add(\\_SB.MAPA, %d), 8", cpu*8);
201 
202         push_block("Field", "MATR, ByteAcc, NoLock, Preserve");
203         indent(); printf("MAT, 64\n");
204         pop_block();
205 
206         push_block("Field", "MATR, ByteAcc, NoLock, Preserve");
207         indent(); printf("Offset(4),\n");
208         indent(); printf("FLG, 1\n");
209         pop_block();
210 
211         push_block("Method", "_MAT, 0");
212         if ( cpu )
213             stmt("Return", "PMAT (%d, MAT)", cpu);
214         else
215             stmt("Return", "ToBuffer(MAT)");
216         pop_block();
217 
218         push_block("Method", "_STA");
219         if ( cpu )
220             push_block("If", "LLess(%d, \\_SB.NCPU)", cpu);
221         push_block("If", "FLG");
222         stmt("Return", "0xF");
223         pop_block();
224         if ( cpu )
225             pop_block();
226         stmt("Return", "0x0");
227         pop_block();
228 
229         push_block("Method", "_EJ0, 1, NotSerialized");
230         stmt("Sleep", "0xC8");
231         pop_block();
232 
233         pop_block();
234     }
235 
236 #ifdef CONFIG_ARM_64
237     pop_block();
238     /**** Processor end ****/
239 #else
240 
241     /* Operation Region 'PRST': bitmask of online CPUs. */
242     stmt("OperationRegion", "PRST, SystemIO, %#x, %d",
243         XEN_ACPI_CPU_MAP, XEN_ACPI_CPU_MAP_LEN);
244     push_block("Field", "PRST, ByteAcc, NoLock, Preserve");
245     indent(); printf("PRS, %u\n", max_cpus);
246     pop_block();
247 
248     /* Control method 'PRSC': CPU hotplug GPE handler. */
249     push_block("Method", "PRSC, 0");
250     stmt("Store", "ToBuffer(PRS), Local0");
251     for ( cpu = 0; cpu < max_cpus; cpu++ )
252     {
253         /* Read a byte at a time from the PRST online-CPU bitmask. */
254         if ( (cpu & 7) == 0 )
255             stmt("Store", "DerefOf(Index(Local0, %u)), Local1", cpu/8);
256         else
257             stmt("ShiftRight", "Local1, 1, Local1");
258         /* Extract current CPU's status: 0=offline; 1=online. */
259         stmt("And", "Local1, 1, Local2");
260         /* Check if status is up-to-date in the relevant MADT LAPIC entry... */
261         push_block("If", "LNotEqual(Local2, \\_SB.PR%02X.FLG)", cpu);
262         /* ...If not, update it and the MADT checksum, and notify OSPM. */
263         stmt("Store", "Local2, \\_SB.PR%02X.FLG", cpu);
264         push_block("If", "LEqual(Local2, 1)");
265         stmt("Notify", "PR%02X, 1", cpu); /* Notify: Device Check */
266         stmt("Subtract", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */
267         pop_block();
268         push_block("Else", NULL);
269         stmt("Notify", "PR%02X, 3", cpu); /* Notify: Eject Request */
270         stmt("Add", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */
271         pop_block();
272         pop_block();
273     }
274     stmt("Return", "One");
275     pop_block();
276 
277     pop_block();
278 
279     /* Define GPE control method. */
280     push_block("Scope", "\\_GPE");
281     push_block("Method",
282                dm_version == QEMU_XEN_TRADITIONAL ? "_L%02d" : "_E%02d",
283                XEN_ACPI_GPE0_CPUHP_BIT);
284     stmt("\\_SB.PRSC ()", NULL);
285     pop_block();
286     pop_block();
287 
288     if (dm_version == QEMU_NONE) {
289         pop_block();
290         return 0;
291     }
292     /**** Processor end ****/
293 
294 
295     /**** PCI0 start ****/
296     push_block("Scope", "\\_SB.PCI0");
297 
298     /*
299      * Reserve the IO port ranges [0x10c0, 0x1101] and [0xb044, 0xb047].
300      * Or else, for a hotplugged-in device, the port IO BAR assigned
301      * by guest OS may conflict with the ranges here.
302      */
303     push_block("Device", "HP0"); {
304         stmt("Name", "_HID, EISAID(\"PNP0C02\")");
305         if (dm_version == QEMU_XEN_TRADITIONAL) {
306             stmt("Name", "_CRS, ResourceTemplate() {"
307                  "  IO (Decode16, 0x10c0, 0x10c0, 0x00, 0x82)"
308                  "  IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
309                  "}");
310         } else {
311             stmt("Name", "_CRS, ResourceTemplate() {"
312                  "  IO (Decode16, 0xae00, 0xae00, 0x00, 0x10)"
313                  "  IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)"
314                  "}");
315         }
316     } pop_block();
317 
318     /*** PCI-ISA link definitions ***/
319     /* BUFA: List of ISA IRQs available for linking to PCI INTx. */
320     stmt("Name", "BUFA, ResourceTemplate() { "
321          "IRQ(Level, ActiveLow, Shared) { 5, 10, 11 } }");
322     /* BUFB: IRQ descriptor for returning from link-device _CRS methods. */
323     stmt("Name", "BUFB, Buffer() { "
324          "0x23, 0x00, 0x00, 0x18, " /* IRQ descriptor */
325          "0x79, 0 }");              /* End tag, null checksum */
326     stmt("CreateWordField", "BUFB, 0x01, IRQV");
327     /* Create four PCI-ISA link devices: LNKA, LNKB, LNKC, LNKD. */
328     for ( link = 0; link < 4; link++ )
329     {
330         push_block("Device", "LNK%c", 'A'+link);
331         stmt("Name", "_HID,  EISAID(\"PNP0C0F\")");  /* PCI interrupt link */
332         stmt("Name", "_UID, %u", link+1);
333         push_block("Method", "_STA, 0");
334         push_block("If", "And(PIR%c, 0x80)", 'A'+link);
335         stmt("Return", "0x09");
336         pop_block();
337         push_block("Else", NULL);
338         stmt("Return", "0x0B");
339         pop_block();
340         pop_block();
341         push_block("Method", "_PRS");
342         stmt("Return", "BUFA");
343         pop_block();
344         push_block("Method", "_DIS");
345         stmt("Or", "PIR%c, 0x80, PIR%c", 'A'+link, 'A'+link);
346         pop_block();
347         push_block("Method", "_CRS");
348         stmt("And", "PIR%c, 0x0f, Local0", 'A'+link);
349         stmt("ShiftLeft", "0x1, Local0, IRQV");
350         stmt("Return", "BUFB");
351         pop_block();
352         push_block("Method", "_SRS, 1");
353         stmt("CreateWordField", "ARG0, 0x01, IRQ1");
354         stmt("FindSetRightBit", "IRQ1, Local0");
355         stmt("Decrement", "Local0");
356         stmt("Store", "Local0, PIR%c", 'A'+link);
357         pop_block();
358         pop_block();
359     }
360 
361     /*** PCI interrupt routing definitions***/
362     /* _PRT: Method to return routing table. */
363     push_block("Method", "_PRT, 0");
364     push_block("If", "PICD");
365     stmt("Return", "PRTA");
366     pop_block();
367     stmt("Return", "PRTP");
368     pop_block();
369     /* PRTP: PIC routing table (via ISA links). */
370     printf("Name(PRTP, Package() {\n");
371     for ( dev = 1; dev < 32; dev++ )
372         for ( intx = 0; intx < 4; intx++ ) /* INTA-D */
373             printf("Package(){0x%04xffff, %u, \\_SB.PCI0.LNK%c, 0},\n",
374                    dev, intx, 'A'+((dev+intx)&3));
375     printf("})\n");
376     /* PRTA: APIC routing table (via non-legacy IOAPIC GSIs). */
377     printf("Name(PRTA, Package() {\n");
378     for ( dev = 1; dev < 32; dev++ )
379         for ( intx = 0; intx < 4; intx++ ) /* INTA-D */
380             printf("Package(){0x%04xffff, %u, 0, %u},\n",
381                    dev, intx, ((dev*4+dev/8+intx)&31)+16);
382     printf("})\n");
383 
384     /*
385      * Each PCI hotplug slot needs at least two methods to handle
386      * the ACPI event:
387      *  _EJ0: eject a device
388      *  _STA: return a device's status, e.g. enabled or removed
389      *
390      * Eject button would generate a general-purpose event, then the
391      * control method for this event uses Notify() to inform OSPM which
392      * action happened and on which device.
393      *
394      * Pls. refer "6.3 Device Insertion, Removal, and Status Objects"
395      * in ACPI spec 3.0b for details.
396      *
397      * QEMU provides a simple hotplug controller with some I/O to handle
398      * the hotplug action and status, which is beyond the ACPI scope.
399      */
400     if (dm_version == QEMU_XEN_TRADITIONAL) {
401         for ( slot = 0; slot < 0x100; slot++ )
402         {
403             push_block("Device", "S%02X", slot);
404             /* _ADR == dev:fn (16:16) */
405             stmt("Name", "_ADR, 0x%08x", ((slot & ~7) << 13) | (slot & 7));
406             /* _SUN == dev */
407             stmt("Name", "_SUN, 0x%08x", slot >> 3);
408             push_block("Method", "_EJ0, 1");
409             if (debug)
410             {
411                 stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
412                 stmt("Store", "0x88, \\_GPE.DPT2");
413             }
414             stmt("Store", "0x%02x, \\_GPE.PH%02X", /* eject */
415                  (slot & 1) ? 0x10 : 0x01, slot & ~1);
416             pop_block();
417             push_block("Method", "_STA, 0");
418             if (debug)
419             {
420                 stmt("Store", "0x%02x, \\_GPE.DPT1", slot);
421                 stmt("Store", "0x89, \\_GPE.DPT2");
422             }
423             if ( slot & 1 )
424                 stmt("ShiftRight", "\\_GPE.PH%02X, 0x04, Local1", slot & ~1);
425             else
426                 stmt("And", "\\_GPE.PH%02X, 0x0f, Local1", slot & ~1);
427             stmt("Return", "Local1"); /* IN status as the _STA */
428             pop_block();
429             pop_block();
430         }
431     } else {
432         stmt("OperationRegion", "SEJ, SystemIO, 0xae08, 0x08");
433         push_block("Field", "SEJ, DWordAcc, NoLock, WriteAsZeros");
434         indent(); printf("B0EJ, 32,\n");
435         indent(); printf("B0RM, 32,\n");
436         pop_block();
437 
438         /* hotplug_slot */
439         for (slot = 1; slot <= 31; slot++) {
440             push_block("Device", "S%i", slot); {
441                 stmt("Name", "_ADR, %#06x0000", slot);
442                 push_block("Method", "_EJ0,1"); {
443                     stmt("Store", "%#010x, B0EJ", 1 << slot);
444                 } pop_block();
445                 stmt("Name", "_SUN, %i", slot);
446                 push_block("Method", "_STA, 0"); {
447                     push_block("If", "And(B0RM, ShiftLeft(1, %i))", slot);
448                     stmt("Return", "0xF");
449                     pop_block();
450                     stmt("Return", "0x0");
451                 } pop_block();
452             } pop_block();
453         }
454     }
455 
456     pop_block();
457     /**** PCI0 end ****/
458 
459 
460     /**** GPE start ****/
461     push_block("Scope", "\\_GPE");
462 
463     if (dm_version == QEMU_XEN_TRADITIONAL) {
464         stmt("OperationRegion", "PHP, SystemIO, 0x10c0, 0x82");
465 
466         push_block("Field", "PHP, ByteAcc, NoLock, Preserve");
467         indent(); printf("PSTA, 8,\n"); /* hotplug controller event reg */
468         indent(); printf("PSTB, 8,\n"); /* hotplug controller slot reg */
469         for ( slot = 0; slot < 0x100; slot += 2 )
470         {
471             indent();
472             /* Each hotplug control register manages a pair of pci functions. */
473             printf("PH%02X, 8,\n", slot);
474         }
475         pop_block();
476     } else {
477         stmt("OperationRegion", "PCST, SystemIO, 0xae00, 0x08");
478         push_block("Field", "PCST, DWordAcc, NoLock, WriteAsZeros");
479         indent(); printf("PCIU, 32,\n");
480         indent(); printf("PCID, 32,\n");
481         pop_block();
482     }
483 
484     stmt("OperationRegion", "DG1, SystemIO, 0xb044, 0x04");
485 
486     push_block("Field", "DG1, ByteAcc, NoLock, Preserve");
487     indent(); printf("DPT1, 8, DPT2, 8\n");
488     pop_block();
489 
490     if (dm_version == QEMU_XEN_TRADITIONAL) {
491         push_block("Method", "_L03, 0, Serialized");
492         /* Detect slot and event (remove/add). */
493         stmt("Name", "SLT, 0x0");
494         stmt("Name", "EVT, 0x0");
495         stmt("Store", "PSTA, Local1");
496         stmt("And", "Local1, 0xf, EVT");
497         stmt("Store", "PSTB, Local1"); /* XXX: Store (PSTB, SLT) ? */
498         stmt("And", "Local1, 0xff, SLT");
499         if (debug)
500         {
501             stmt("Store", "SLT, DPT1");
502             stmt("Store", "EVT, DPT2");
503         }
504         /* Decision tree */
505         decision_tree(0x00, 0x100, "SLT", pci_hotplug_notify);
506         pop_block();
507     } else {
508         push_block("Method", "_E01");
509         for (slot = 1; slot <= 31; slot++) {
510             push_block("If", "And(PCIU, ShiftLeft(1, %i))", slot);
511             stmt("Notify", "\\_SB.PCI0.S%i, 1", slot);
512             pop_block();
513             push_block("If", "And(PCID, ShiftLeft(1, %i))", slot);
514             stmt("Notify", "\\_SB.PCI0.S%i, 3", slot);
515             pop_block();
516         }
517         pop_block();
518     }
519 
520     pop_block();
521     /**** GPE end ****/
522 #endif
523 
524     pop_block();
525     /**** DSDT DefinitionBlock end ****/
526 
527     return 0;
528 }
529 
530 /*
531  * Local variables:
532  * mode: C
533  * c-file-style: "BSD"
534  * c-basic-offset: 4
535  * tab-width: 4
536  * indent-tabs-mode: nil
537  * End:
538  */
539