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