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