blob: 2ac82821e7f93a9bf3c755484381392c806038cd [file] [log] [blame]
Gerd Hoffmann654ba382020-03-24 12:13:32 +01001// Support for parsing dsdt acpi tables
2//
3// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "config.h" // CONFIG_*
8#include "list.h" // hlist_*
9#include "malloc.h" // malloc_*
10#include "output.h" // dprintf
11#include "string.h" // memcpy
12#include "util.h"
13#include "std/acpi.h" // struct rsdp_descriptor
14
15/****************************************************************
16 * DSDT parser
17 ****************************************************************/
18
19struct acpi_device {
20 struct hlist_node node;
21 char name[16];
22 u8 *hid_aml;
23 u8 *sta_aml;
24 u8 *crs_data;
25 int crs_size;
26};
27static struct hlist_head acpi_devices VARVERIFY32INIT;
28static const int parse_dumpdevs = 0;
29
30struct parse_state {
31 char name[32];
32 struct acpi_device *dev;
33 int error;
34 int depth;
35};
36
37static void parse_termlist(struct parse_state *s,
38 u8 *ptr, int offset, int pkglength);
39
40static void hex(const u8 *ptr, int count, int lvl, const char *item)
41{
42 int l = 0, i;
43
44 do {
45 dprintf(lvl, "%s: %04x: ", item, l);
46 for (i = l; i < l+16; i += 4)
47 dprintf(lvl, "%02x %02x %02x %02x ",
48 ptr[i+0], ptr[i+1], ptr[i+2], ptr[i+3]);
49 for (i = l; i < l+16; i++)
50 dprintf(lvl, "%c", (ptr[i] > 0x20 && ptr[i] < 0x80)
51 ? ptr[i] : '.');
52 dprintf(lvl, "\n");
53 l += 16;
54 } while (l < count);
55}
56
57static u64 parse_resource_int(u8 *ptr, int count)
58{
59 u64 value = 0;
60 int index = 0;
61
62 for (index = 0; index < count; index++)
63 value |= (u64)ptr[index] << (index * 8);
64 return value;
65}
66
67static int parse_resource_bit(u8 *ptr, int count)
68{
69 int bit;
70
71 for (bit = 0; bit < count*8; bit++)
72 if (ptr[bit/8] & (1 << (bit%8)))
73 return bit;
74 return 0;
75}
76
77static int parse_resource(u8 *ptr, int length, int *type, u64 *min, u64 *max)
78{
79 int rname, rsize;
80 u64 len;
81
82 *type = -1;
83 *min = 0;
84 *max = 0;
85 len = 0;
86 if (!(ptr[0] & 0x80)) {
87 /* small resource */
88 rname = (ptr[0] >> 3) & 0x0f;
89 rsize = ptr[0] & 0x07;
90 rsize++;
91 switch (rname) {
92 case 0x04: /* irq */
93 *min = parse_resource_bit(ptr + 1, rsize);
94 *max = *min;
95 *type = 3;
96 break;
97 case 0x0f: /* end marker */
98 return 0;
99 case 0x08: /* io */
100 *min = parse_resource_int(ptr + 2, 2);
101 *max = parse_resource_int(ptr + 4, 2);
102 if (*min == *max) {
103 *max = *min + ptr[7] - 1;
104 *type = 1;
105 }
106 break;
107 case 0x09: /* fixed io */
108 *min = parse_resource_int(ptr + 2, 2);
109 *max = *min + ptr[4] - 1;
110 *type = 1;
111 break;
112 default:
113 dprintf(3, "%s: small: 0x%x (len %d)\n",
114 __func__, rname, rsize);
115 break;
116 }
117 } else {
118 /* large resource */
119 rname = ptr[0] & 0x7f;
120 rsize = ptr[2] << 8 | ptr[1];
121 rsize += 3;
122 switch (rname) {
123 case 0x06: /* 32-bit Fixed Location Memory Range Descriptor */
124 *min = parse_resource_int(ptr + 4, 4);
125 len = parse_resource_int(ptr + 8, 4);
126 *max = *min + len - 1;
127 *type = 0;
128 break;
129 case 0x07: /* DWORD Address Space Descriptor */
130 *min = parse_resource_int(ptr + 10, 4);
131 *max = parse_resource_int(ptr + 14, 4);
132 *type = ptr[3];
133 break;
134 case 0x08: /* WORD Address Space Descriptor */
135 *min = parse_resource_int(ptr + 8, 2);
136 *max = parse_resource_int(ptr + 10, 2);
137 *type = ptr[3];
138 break;
139 case 0x09: /* irq */
140 *min = parse_resource_int(ptr + 5, 4);
141 *max = *min;
142 *type = 3;
143 break;
144 case 0x0a: /* QWORD Address Space Descriptor */
145 *min = parse_resource_int(ptr + 14, 8);
146 *max = parse_resource_int(ptr + 22, 8);
147 *type = ptr[3];
148 break;
149 default:
150 dprintf(3, "%s: large: 0x%x (len %d)\n", __func__, rname, rsize);
151 break;
152 }
153 }
154 return rsize;
155}
156
157static int find_resource(u8 *ptr, int len, int kind, u64 *min, u64 *max)
158{
159 int type, size, offset = 0;
160
161 do {
162 size = parse_resource(ptr + offset, len - offset,
163 &type, min, max);
164 if (kind == type)
165 return 0;
166 offset += size;
167 } while (size > 0 && offset < len);
168 return -1;
169}
170
171static int print_resources(const char *prefix, u8 *ptr, int len)
172{
173 static const char *typename[] = { "mem", "i/o", "bus" };
174 int type, size, offset = 0;
175 u64 min, max;
176
177 do {
178 size = parse_resource(ptr + offset, len - offset,
179 &type, &min, &max);
180 switch (type) {
181 case 0:
182 case 1:
183 case 2:
184 dprintf(1, "%s%s 0x%llx -> 0x%llx\n",
185 prefix, typename[type], min, max);
186 break;
187 case 3:
188 dprintf(1, "%sirq %lld\n", prefix, min);
189 break;
190 }
191 offset += size;
192 } while (size > 0 && offset < len);
193 return -1;
194}
195
196static int parse_nameseg(u8 *ptr, char **dst)
197{
198 if (dst && *dst) {
199 *(dst[0]++) = ptr[0];
200 if (ptr[1] != '_')
201 *(dst[0]++) = ptr[1];
202 if (ptr[2] != '_')
203 *(dst[0]++) = ptr[2];
204 if (ptr[3] != '_')
205 *(dst[0]++) = ptr[3];
206 *(dst[0]) = 0;
207 }
208 return 4;
209}
210
211static int parse_namestring(struct parse_state *s,
212 u8 *ptr, const char *item)
213{
214 char *dst = s->name;
215 int offset = 0;
216 int i, count;
217
218 for (;;) {
219 switch (ptr[offset]) {
220 case 0: /* null name */
221 offset++;
222 *(dst++) = 0;
223 break;
224 case 0x2e:
225 offset++;
226 offset += parse_nameseg(ptr + offset, &dst);
227 *(dst++) = '.';
228 offset += parse_nameseg(ptr + offset, &dst);
229 break;
230 case 0x2f:
231 offset++;
232 count = ptr[offset];
233 offset++;
234 for (i = 0; i < count; i++) {
235 if (i)
236 *(dst++) = '.';
237 offset += parse_nameseg(ptr + offset, &dst);
238 }
239 break;
240 case '\\':
241 *(dst++) = '\\';
242 offset++;
243 continue;
244 case '^':
245 *(dst++) = '^';
246 offset++;
247 continue;
248 case 'A' ... 'Z':
249 case '_':
250 offset += parse_nameseg(ptr, &dst);
251 break;
252 default:
253 hex(ptr, 16, 3, __func__);
254 s->error = 1;
255 break;
256 }
257 break;
258 }
259 dprintf(5, "%s: %d %s '%s'\n", __func__, s->depth,
260 item, s->name);
261 return offset;
262}
263
264static int parse_termarg_int(u8 *ptr, int *error, u64 *dst)
265{
266 u64 value;
267 int offset = 1;
268
269 switch (ptr[0]) {
270 case 0x00: /* zero */
271 value = 0;
272 break;
273 case 0x01: /* one */
274 value = 1;
275 break;
276 case 0x0a: /* byte prefix */
277 value = ptr[1];
278 offset++;
279 break;
280 case 0x0b: /* word prefix */
281 value = ptr[1] |
282 ((unsigned long)ptr[2] << 8);
283 offset += 2;
284 break;
285 case 0x0c: /* dword prefix */
286 value = ptr[1] |
287 ((unsigned long)ptr[2] << 8) |
288 ((unsigned long)ptr[3] << 16) |
289 ((unsigned long)ptr[4] << 24);
290 offset += 4;
291 break;
292 default:
293 value = 0;
294 hex(ptr, 16, 3, __func__);
295 if (error)
296 *error = 1;
297 break;
298 }
299
300 if (dst)
301 *dst = value;
302 dprintf(5, "%s: 0x%llx\n", __func__, value);
303 return offset;
304}
305
306static int parse_pkglength(u8 *ptr, int *pkglength)
307{
308 int offset = 2;
309
310 *pkglength = 0;
311 switch (ptr[0] >> 6) {
312 case 3:
313 *pkglength |= ptr[3] << 20;
314 offset++;
315 case 2:
316 *pkglength |= ptr[2] << 12;
317 offset++;
318 case 1:
319 *pkglength |= ptr[1] << 4;
320 *pkglength |= ptr[0] & 0x0f;
321 return offset;
322 case 0:
323 default:
324 *pkglength |= ptr[0] & 0x3f;
325 return 1;
326 }
327}
328
329static int parse_pkg_common(struct parse_state *s,
330 u8 *ptr, const char *item, int *pkglength)
331{
332 int offset;
333
334 offset = parse_pkglength(ptr, pkglength);
335 offset += parse_namestring(s, ptr + offset, item);
336 return offset;
337}
338
339static int parse_pkg_scope(struct parse_state *s,
340 u8 *ptr)
341{
342 int offset, pkglength;
343
344 offset = parse_pkg_common(s, ptr, "scope", &pkglength);
345 parse_termlist(s, ptr, offset, pkglength);
346 return pkglength;
347}
348
349static int parse_pkg_device(struct parse_state *s,
350 u8 *ptr)
351{
352 int offset, pkglength;
353
354 offset = parse_pkg_common(s, ptr, "device", &pkglength);
355
356 s->dev = malloc_tmp(sizeof(struct acpi_device));
357 if (!s->dev) {
358 warn_noalloc();
359 s->error = 1;
360 return pkglength;
361 }
362
363 memset(s->dev, 0, sizeof(struct acpi_device));
364 hlist_add_head(&s->dev->node, &acpi_devices);
365 strtcpy(s->dev->name, s->name, sizeof(s->name));
366 parse_termlist(s, ptr, offset, pkglength);
367 s->dev = NULL;
368
369 return pkglength;
370}
371
372static int parse_pkg_buffer(struct parse_state *s,
373 u8 *ptr)
374{
375 u64 blen;
376 int pkglength, offset;
377
378 offset = parse_pkglength(ptr, &pkglength);
379 offset += parse_termarg_int(ptr + offset, &s->error, &blen);
380 if (s->dev && strcmp(s->name, "_CRS") == 0) {
381 s->dev->crs_data = ptr + offset;
382 s->dev->crs_size = blen;
383 }
384 return pkglength;
385}
386
387static int parse_pkg_skip(struct parse_state *s,
388 u8 *ptr, int op, int name)
389{
390 int pkglength, offset;
391 char item[8];
392
393 snprintf(item, sizeof(item), "op %x", op);
394 offset = parse_pkglength(ptr, &pkglength);
395 if (name) {
396 parse_namestring(s, ptr + offset, item);
397 } else {
398 dprintf(5, "%s: %s (%d)\n", __func__, item, pkglength);
399 }
400 return pkglength;
401}
402
403static int parse_termobj(struct parse_state *s,
404 u8 *ptr)
405{
406 int offset = 1;
407
408 if (s->depth == 16) {
409 dprintf(1, "%s: deep recursion\n", __func__);
410 s->error = 1;
411 return offset;
412 }
413
414 s->depth++;
415 switch (ptr[0]) {
416 case 0x00: /* zero */
417 break;
418 case 0x01: /* one */
419 break;
Igor Mammedov61e901b2022-11-18 15:27:55 +0100420 case 0x06: /* AliasOp */
421 offset += parse_namestring(s, ptr + offset, "SourceObject");
422 offset += parse_namestring(s, ptr + offset, "AliasObject");
423 break;
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100424 case 0x08: /* name op */
425 offset += parse_namestring(s, ptr + offset, "name");
426 offset += parse_termobj(s, ptr + offset);
427 if (s->dev && strcmp(s->name, "_HID") == 0)
428 s->dev->hid_aml = ptr;
429 if (s->dev && strcmp(s->name, "_STA") == 0)
430 s->dev->sta_aml = ptr;
431 break;
432 case 0x0a: /* byte prefix */
433 offset++;
434 break;
435 case 0x0b: /* word prefix */
436 offset += 2;
437 break;
438 case 0x0c: /* dword prefix */
439 offset += 4;
440 break;
441 case 0x0d: /* string prefix */
442 while (ptr[offset])
443 offset++;
444 offset++;
445 break;
446 case 0x10: /* scope op */
447 offset += parse_pkg_scope(s, ptr + offset);
448 break;
449 case 0x11: /* buffer op */
450 offset += parse_pkg_buffer(s, ptr + offset);
451 break;
452 case 0x12: /* package op */
453 case 0x13: /* var package op */
454 offset += parse_pkg_skip(s, ptr + offset, ptr[0], 0);
455 break;
456 case 0x14: /* method op */
457 offset += parse_pkg_skip(s, ptr + offset, ptr[0], 1);
458 if (s->dev && strcmp(s->name, "_STA") == 0)
459 s->dev->sta_aml = ptr;
460 break;
461 case 0x5b: /* ext op prefix */
462 offset++;
463 switch (ptr[1]) {
464 case 0x01: /* mutex op */
465 offset += parse_namestring(s, ptr + offset, "mutex");
466 offset++; /* sync flags */
467 break;
468 case 0x80: /* op region op */
469 offset += parse_namestring(s, ptr + offset, "op region");
470 offset++; /* region space */
471 offset += parse_termarg_int(ptr + offset, &s->error, NULL);
472 offset += parse_termarg_int(ptr + offset, &s->error, NULL);
473 break;
474 case 0x81: /* field op */
475 case 0x83: /* processor op */
476 case 0x84: /* power resource op */
477 case 0x85: /* thermal zone op */
478 offset += parse_pkg_skip(s, ptr + offset, 0x5b00 | ptr[1], 1);
479 break;
480 case 0x82: /* device op */
481 offset += parse_pkg_device(s, ptr + offset);
482 break;
483 default:
484 hex(ptr, 16, 3, __func__);
485 s->error = 1;
486 break;
487 }
488 break;
489 default:
490 hex(ptr, 16, 3, __func__);
491 s->error = 1;
492 break;
493 }
494 s->depth--;
495
496 return offset;
497}
498
499static void parse_termlist(struct parse_state *s,
500 u8 *ptr, int offset, int pkglength)
501{
502 for (;;) {
503 offset += parse_termobj(s, ptr + offset);
504 if (offset == pkglength)
505 return;
506 if (offset > pkglength) {
507 dprintf(1, "%s: overrun: %d/%d\n", __func__,
508 offset, pkglength);
509 s->error = 1;
510 return;
511 }
512 if (s->error) {
513 dprintf(1, "%s: parse error, skip from %d/%d\n", __func__,
514 offset, pkglength);
515 s->error = 0;
516 return;
517 }
518 }
519}
520
521static struct acpi_device *acpi_dsdt_find(struct acpi_device *prev,
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200522 const u8 *aml1, int size1,
523 const u8 *aml2, int size2)
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100524{
525 struct acpi_device *dev;
526 struct hlist_node *node;
527
528 if (!prev)
529 node = acpi_devices.first;
530 else
531 node = prev->node.next;
532
533 for (; node != NULL; node = dev->node.next) {
534 dev = container_of(node, struct acpi_device, node);
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200535 if (!aml1 && !aml2)
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100536 return dev;
537 if (!dev->hid_aml)
538 continue;
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200539 if (aml1 && memcmp(dev->hid_aml + 5, aml1, size1) == 0)
540 return dev;
541 if (aml2 && memcmp(dev->hid_aml + 5, aml2, size2) == 0)
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100542 return dev;
543 }
544 return NULL;
545}
546
547static int acpi_dsdt_present(struct acpi_device *dev)
548{
549 if (!dev)
550 return 0; /* no */
551 if (!dev->sta_aml)
552 return 1; /* yes */
553 if (dev->sta_aml[0] == 0x14)
554 return -1; /* unknown (can't evaluate method) */
555 if (dev->sta_aml[0] == 0x08) {
556 u64 value = 0;
557 parse_termarg_int(dev->sta_aml + 5, NULL, &value);
558 if (value == 0)
559 return 0; /* no */
560 else
561 return 1; /* yes */
562 }
563 return -1; /* unknown (should not happen) */
564}
565
566/****************************************************************
567 * DSDT parser, public interface
568 ****************************************************************/
569
570struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev,
571 const char *hid)
572{
573 if (!CONFIG_ACPI_PARSE)
574 return NULL;
575
576 u8 aml[10];
577 int len = snprintf((char*)aml, sizeof(aml), "\x0d%s", hid);
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200578 return acpi_dsdt_find(prev, aml, len, NULL, 0);
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100579}
580
581struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid)
582{
583 if (!CONFIG_ACPI_PARSE)
584 return NULL;
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200585 u8 aml1[] = {
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100586 0x0c, 0x41, 0xd0,
587 eisaid >> 8,
588 eisaid & 0xff
589 };
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200590 u8 aml2[10];
591 int len2 = snprintf((char*)aml2, sizeof(aml2), "\x0dPNP%04X", eisaid);
592 return acpi_dsdt_find(prev, aml1, 5, aml2, len2);
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100593}
594
595char *acpi_dsdt_name(struct acpi_device *dev)
596{
597 if (!CONFIG_ACPI_PARSE || !dev)
598 return NULL;
599 return dev->name;
600}
601
602int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max)
603{
604 if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
605 return -1;
606 return find_resource(dev->crs_data, dev->crs_size,
607 1 /* I/O */, min, max);
608}
609
610int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max)
611{
612 if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
613 return -1;
614 return find_resource(dev->crs_data, dev->crs_size,
615 0 /* mem */, min, max);
616}
617
618int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq)
619{
620 u64 max;
621 if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
622 return -1;
623 return find_resource(dev->crs_data, dev->crs_size,
624 3 /* irq */, irq, &max);
625}
626
627int acpi_dsdt_present_eisaid(u16 eisaid)
628{
629 if (!CONFIG_ACPI_PARSE)
630 return -1; /* unknown */
631 if (hlist_empty(&acpi_devices))
632 return -1; /* unknown (no dsdt table) */
633
634 struct acpi_device *dev = acpi_dsdt_find_eisaid(NULL, eisaid);
635 return acpi_dsdt_present(dev);
636}
637
638void acpi_dsdt_parse(void)
639{
640 if (!CONFIG_ACPI_PARSE)
641 return;
642
643 struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
644 if (!fadt)
645 return;
646 u8 *dsdt = (void*)(fadt->dsdt);
647 if (!dsdt)
648 return;
649
650 u32 length = *(u32*)(dsdt + 4);
651 u32 offset = 0x24;
652 dprintf(1, "ACPI: parse DSDT at %p (len %d)\n", dsdt, length);
653
654 struct parse_state s;
655 memset(&s, 0, sizeof(s));
656 parse_termlist(&s, dsdt, offset, length);
657
658 if (!parse_dumpdevs)
659 return;
660
661 struct acpi_device *dev;
662 dprintf(1, "ACPI: dumping dsdt devices\n");
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200663 for (dev = acpi_dsdt_find(NULL, NULL, 0, NULL, 0);
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100664 dev != NULL;
Gerd Hoffmanndfac05b2020-09-30 13:12:22 +0200665 dev = acpi_dsdt_find(dev, NULL, 0, NULL, 0)) {
Gerd Hoffmann654ba382020-03-24 12:13:32 +0100666 dprintf(1, " %s", acpi_dsdt_name(dev));
667 if (dev->hid_aml)
668 dprintf(1, ", hid");
669 if (dev->sta_aml)
670 dprintf(1, ", sta (0x%x)", dev->sta_aml[0]);
671 if (dev->crs_data)
672 dprintf(1, ", crs");
673 dprintf(1, "\n");
674 if (dev->crs_data)
675 print_resources(" ", dev->crs_data, dev->crs_size);
676 }
677}