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