1#include <uacpi/internal/tables.h>
2#include <uacpi/internal/utilities.h>
3#include <uacpi/internal/stdlib.h>
4#include <uacpi/internal/interpreter.h>
5
6#ifndef UACPI_STATIC_TABLE_ARRAY_LEN
7 #define UACPI_STATIC_TABLE_ARRAY_LEN 16
8#endif
9
10DYNAMIC_ARRAY_WITH_INLINE_STORAGE(
11 table_array, struct uacpi_installed_table, UACPI_STATIC_TABLE_ARRAY_LEN
12)
13DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
14 table_array, struct uacpi_installed_table,
15)
16
17static struct table_array tables;
18static uacpi_table_installation_handler installation_handler;
19static uacpi_handle table_mutex;
20
21uacpi_status uacpi_initialize_tables(void)
22{
23 table_mutex = uacpi_kernel_create_mutex();
24 if (uacpi_unlikely(table_mutex == UACPI_NULL))
25 return UACPI_STATUS_OUT_OF_MEMORY;
26
27 return UACPI_STATUS_OK;
28}
29
30uacpi_status uacpi_set_table_installation_handler(
31 uacpi_table_installation_handler handler
32)
33{
34 uacpi_status ret = UACPI_STATUS_OK;
35
36 /*
37 * The mutex might not exist yet because uacpi_initialize_tables might not
38 * have been called at this point, allow that possibility since the user
39 * might want to install this handler early.
40 */
41 if (table_mutex != UACPI_NULL)
42 UACPI_MUTEX_ACQUIRE(table_mutex);
43
44 if (installation_handler != UACPI_NULL && handler != UACPI_NULL)
45 goto out;
46
47 installation_handler = handler;
48
49out:
50 if (table_mutex != UACPI_NULL)
51 UACPI_MUTEX_RELEASE(table_mutex);
52 return ret;
53}
54
55static uacpi_status initialize_fadt(struct acpi_sdt_hdr*);
56
57static uacpi_object_name fadt_signature = {
58 .text = { ACPI_FADT_SIGNATURE },
59};
60
61static uacpi_object_name dsdt_signature = {
62 .text = { ACPI_DSDT_SIGNATURE },
63};
64
65static uacpi_object_name facs_signature = {
66 .text = { ACPI_FACS_SIGNATURE },
67};
68
69static uacpi_u8 table_checksum(void *table, uacpi_size size)
70{
71 uacpi_u8 *bytes = table;
72 uacpi_u8 csum = 0;
73 uacpi_size i;
74
75 for (i = 0; i < size; ++i)
76 csum += bytes[i];
77
78 return csum;
79}
80
81uacpi_status uacpi_verify_table_checksum(void *table, uacpi_size size)
82{
83 uacpi_status ret = UACPI_STATUS_OK;
84 uacpi_u8 csum;
85
86 csum = table_checksum(table, size);
87
88 if (uacpi_unlikely(csum != 0)) {
89 enum uacpi_log_level lvl = UACPI_LOG_WARN;
90 struct acpi_sdt_hdr *hdr = table;
91
92 if (uacpi_check_flag(UACPI_FLAG_BAD_CSUM_FATAL)) {
93 ret = UACPI_STATUS_BAD_CHECKSUM;
94 lvl = UACPI_LOG_ERROR;
95 }
96
97 uacpi_log_lvl(
98 lvl, "invalid table "UACPI_PRI_TBL_HDR" checksum %d!\n",
99 UACPI_FMT_TBL_HDR(hdr), csum
100 );
101 }
102
103 return ret;
104}
105
106uacpi_status uacpi_check_table_signature(void *table, const uacpi_char *expect)
107{
108 uacpi_status ret = UACPI_STATUS_OK;
109
110 if (uacpi_memcmp(lhs: table, rhs: expect, count: 4) != 0) {
111 enum uacpi_log_level lvl = UACPI_LOG_WARN;
112 struct acpi_sdt_hdr *hdr = table;
113
114 if (uacpi_check_flag(UACPI_FLAG_BAD_TBL_SIGNATURE_FATAL)) {
115 ret = UACPI_STATUS_INVALID_SIGNATURE;
116 lvl = UACPI_LOG_ERROR;
117 }
118
119 uacpi_log_lvl(
120 lvl,
121 "invalid table "UACPI_PRI_TBL_HDR" signature (expected '%.4s')\n",
122 UACPI_FMT_TBL_HDR(hdr), expect
123 );
124 }
125
126 return ret;
127}
128
129static uacpi_status table_alloc(
130 struct uacpi_installed_table **out_tbl, uacpi_size *out_idx
131)
132{
133 struct uacpi_installed_table *tbl;
134
135 tbl = table_array_alloc(arr: &tables);
136 if (uacpi_unlikely(tbl == UACPI_NULL))
137 return UACPI_STATUS_OUT_OF_MEMORY;
138
139 *out_tbl = tbl;
140 *out_idx = table_array_size(arr: &tables) - 1;
141 return UACPI_STATUS_OK;
142}
143
144static uacpi_status
145get_external_table_signature_and_length(uacpi_phys_addr phys_addr,
146 uacpi_object_name *out_sign,
147 uacpi_u32 *out_len)
148{
149 struct acpi_sdt_hdr *hdr;
150
151 hdr = uacpi_kernel_map(addr: phys_addr, len: sizeof(struct acpi_sdt_hdr));
152 if (uacpi_unlikely(!hdr))
153 return UACPI_STATUS_MAPPING_FAILED;
154
155 uacpi_memcpy(dest: out_sign, src: hdr->signature, count: sizeof(hdr->signature));
156 *out_len = hdr->length;
157
158 uacpi_kernel_unmap(addr: hdr, len: sizeof(struct acpi_sdt_hdr));
159 return UACPI_STATUS_OK;
160}
161
162static uacpi_status verify_and_install_table(
163 uacpi_object_name signature, uacpi_u32 length,
164 uacpi_phys_addr phys_addr, void *virt_addr, enum uacpi_table_origin origin,
165 uacpi_table *out_table
166)
167{
168 uacpi_status ret;
169 struct uacpi_installed_table *table;
170 uacpi_size idx;
171
172 /*
173 * FACS is the only(?) table without a checksum because it has OSPM
174 * writable fields. Don't try to validate it here.
175 */
176 if (signature.id != facs_signature.id) {
177 ret = uacpi_verify_table_checksum(table: virt_addr, size: length);
178 if (uacpi_unlikely_error(ret))
179 return ret;
180 }
181
182 if (signature.id == dsdt_signature.id) {
183 struct acpi_sdt_hdr *hdr = virt_addr;
184 g_uacpi_rt_ctx.is_rev1 = hdr->revision < 2;
185 }
186
187 if (signature.id == fadt_signature.id) {
188 ret = initialize_fadt(virt_addr);
189 if (uacpi_unlikely_error(ret))
190 return ret;
191 }
192
193 ret = table_alloc(out_tbl: &table, out_idx: &idx);
194 if (uacpi_unlikely_error(ret))
195 return ret;
196
197 table->signature = signature;
198 table->phys_addr = phys_addr;
199 table->ptr = virt_addr;
200 table->length = length;
201 table->flags = 0;
202 table->origin = origin;
203
204 if (out_table != UACPI_NULL) {
205 out_table->ptr = virt_addr;
206 out_table->index = idx;
207 }
208
209 return UACPI_STATUS_OK;
210}
211
212static uacpi_status table_install_physical_with_origin_unlocked(
213 uacpi_phys_addr phys, enum uacpi_table_origin origin,
214 const uacpi_char *expected_signature, uacpi_table *out_table
215);
216static uacpi_status table_install_with_origin_unlocked(
217 void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
218);
219
220static uacpi_status handle_table_override(
221 uacpi_table_installation_disposition disposition, uacpi_u64 address,
222 uacpi_table *out_table
223)
224{
225 uacpi_status ret;
226
227 switch (disposition) {
228 case UACPI_TABLE_INSTALLATION_DISPOSITON_VIRTUAL_OVERRIDE:
229 ret = table_install_with_origin_unlocked(
230 UACPI_VIRT_ADDR_TO_PTR((uacpi_virt_addr)address),
231 origin: UACPI_TABLE_ORIGIN_HOST_VIRTUAL,
232 out_table
233 );
234 return ret;
235 case UACPI_TABLE_INSTALLATION_DISPOSITON_PHYSICAL_OVERRIDE:
236 return table_install_physical_with_origin_unlocked(
237 phys: (uacpi_phys_addr)address,
238 origin: UACPI_TABLE_ORIGIN_HOST_PHYSICAL,
239 UACPI_NULL,
240 out_table
241 );
242 default:
243 uacpi_error("invalid table installation disposition %d\n", disposition);
244 return UACPI_STATUS_INTERNAL_ERROR;
245 }
246}
247
248static uacpi_status table_install_physical_with_origin_unlocked(
249 uacpi_phys_addr phys, enum uacpi_table_origin origin,
250 const uacpi_char *expected_signature, uacpi_table *out_table
251)
252{
253 uacpi_object_name signature;
254 uacpi_u32 length;
255 void *virt;
256 uacpi_status ret;
257
258 ret = get_external_table_signature_and_length(phys_addr: phys, out_sign: &signature, out_len: &length);
259 if (uacpi_unlikely_error(ret))
260 return ret;
261
262 if (uacpi_unlikely(length < sizeof(struct acpi_sdt_hdr))) {
263 uacpi_error("invalid table '%.4s' (0x016%"UACPI_PRIX64") size: %u\n",
264 signature.text, UACPI_FMT64(phys), length);
265 return UACPI_STATUS_INVALID_TABLE_LENGTH;
266 }
267
268 virt = uacpi_kernel_map(addr: phys, len: length);
269 if (uacpi_unlikely(!virt))
270 return UACPI_STATUS_MAPPING_FAILED;
271
272 if (expected_signature != UACPI_NULL) {
273 ret = uacpi_check_table_signature(table: virt, expect: expected_signature);
274 if (uacpi_unlikely_error(ret))
275 goto out;
276 }
277
278 if (origin == UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL &&
279 installation_handler != UACPI_NULL) {
280 uacpi_u64 override;
281 uacpi_table_installation_disposition disposition;
282
283 disposition = installation_handler(virt, &override);
284
285 switch (disposition) {
286 case UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW:
287 break;
288 case UACPI_TABLE_INSTALLATION_DISPOSITON_DENY:
289 uacpi_info(
290 "table '%.4s' (0x016%"UACPI_PRIX64") installation denied "
291 "by host\n", signature.text, UACPI_FMT64(phys)
292 );
293 ret = UACPI_STATUS_DENIED;
294 goto out;
295
296 default:
297 uacpi_info(
298 "table '%.4s' (0x016%"UACPI_PRIX64") installation "
299 "overriden by host\n", signature.text, UACPI_FMT64(phys)
300 );
301
302 ret = handle_table_override(disposition, address: override, out_table);
303 if (uacpi_likely_success(ret))
304 ret = UACPI_STATUS_OVERRIDEN;
305
306 goto out;
307 }
308 }
309
310 ret = verify_and_install_table(signature, length, phys_addr: phys, virt_addr: virt, origin,
311 out_table);
312out:
313 if (uacpi_unlikely_error(ret)) {
314 uacpi_kernel_unmap(addr: virt, len: length);
315 return ret;
316 }
317
318 return UACPI_STATUS_OK;
319}
320
321uacpi_status uacpi_table_install_physical_with_origin(
322 uacpi_phys_addr phys, enum uacpi_table_origin origin, uacpi_table *out_table
323)
324{
325 uacpi_status ret;
326
327 UACPI_MUTEX_ACQUIRE(table_mutex);
328 ret = table_install_physical_with_origin_unlocked(
329 phys, origin, UACPI_NULL, out_table
330 );
331 UACPI_MUTEX_RELEASE(table_mutex);
332
333 return ret;
334}
335
336static uacpi_status table_install_with_origin_unlocked(
337 void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
338)
339{
340 uacpi_object_name signature;
341 struct acpi_sdt_hdr *hdr = virt;
342 uacpi_u32 length;
343
344 uacpi_memcpy(dest: &signature, src: hdr->signature, count: sizeof(hdr->signature));
345 length = hdr->length;
346
347 if (uacpi_unlikely(length < sizeof(struct acpi_sdt_hdr))) {
348 uacpi_error("invalid table '%.4s' (%p) size: %u\n",
349 signature.text, virt, length);
350 return UACPI_STATUS_INVALID_TABLE_LENGTH;
351 }
352
353 if (origin == UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL &&
354 installation_handler != UACPI_NULL) {
355 uacpi_u64 override;
356 uacpi_table_installation_disposition disposition;
357
358 disposition = installation_handler(virt, &override);
359
360 switch (disposition) {
361 case UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW:
362 break;
363 case UACPI_TABLE_INSTALLATION_DISPOSITON_DENY:
364 uacpi_info(
365 "table "UACPI_PRI_TBL_HDR" installation denied by host\n",
366 UACPI_FMT_TBL_HDR(hdr)
367 );
368 return UACPI_STATUS_DENIED;
369
370 default: {
371 uacpi_status ret;
372 uacpi_info(
373 "table "UACPI_PRI_TBL_HDR" installation overriden by host\n",
374 UACPI_FMT_TBL_HDR(hdr)
375 );
376
377 ret = handle_table_override(disposition, address: override, out_table);
378 if (uacpi_likely_success(ret))
379 ret = UACPI_STATUS_OVERRIDEN;
380
381 return ret;
382 }
383 }
384 }
385
386 return verify_and_install_table(
387 signature, length, phys_addr: 0, virt_addr: virt, origin, out_table
388 );
389}
390
391uacpi_status uacpi_table_install_with_origin(
392 void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
393)
394{
395 uacpi_status ret;
396
397 UACPI_MUTEX_ACQUIRE(table_mutex);
398 ret = table_install_with_origin_unlocked(virt, origin, out_table);
399 UACPI_MUTEX_RELEASE(table_mutex);
400
401 return ret;
402}
403
404uacpi_status uacpi_table_install(void *virt, uacpi_table *out_table)
405{
406 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
407
408 return uacpi_table_install_with_origin(
409 virt, origin: UACPI_TABLE_ORIGIN_HOST_VIRTUAL, out_table
410 );
411}
412
413uacpi_status uacpi_table_install_physical(
414 uacpi_phys_addr addr, uacpi_table *out_table
415)
416{
417 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
418
419 return uacpi_table_install_physical_with_origin(
420 phys: addr, origin: UACPI_TABLE_ORIGIN_HOST_PHYSICAL, out_table
421 );
422}
423
424uacpi_status uacpi_for_each_table(
425 uacpi_size base_idx, uacpi_table_iteration_callback cb, void *user
426)
427{
428 uacpi_size idx;
429 struct uacpi_installed_table *tbl;
430 enum uacpi_table_iteration_decision ret;
431
432 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
433
434 UACPI_MUTEX_ACQUIRE(table_mutex);
435
436 for (idx = base_idx; idx < table_array_size(arr: &tables); ++idx) {
437 tbl = table_array_at(arr: &tables, idx);
438
439 ret = cb(user, tbl, idx);
440 if (ret == UACPI_TABLE_ITERATION_DECISION_BREAK)
441 break;
442 }
443
444 UACPI_MUTEX_RELEASE(table_mutex);
445 return UACPI_STATUS_OK;
446}
447
448enum search_type {
449 SEARCH_TYPE_BY_ID,
450 SEARCH_TYPE_MATCH,
451};
452
453struct table_search_ctx {
454 union {
455 const uacpi_table_identifiers *id;
456 uacpi_table_match_callback match_cb;
457 };
458
459 uacpi_table *out_table;
460 uacpi_u8 search_type;
461 uacpi_bool found;
462};
463
464static enum uacpi_table_iteration_decision do_search_tables(
465 void *user, struct uacpi_installed_table *tbl, uacpi_size idx
466)
467{
468 struct table_search_ctx *ctx = user;
469 uacpi_table *out_table;
470
471 switch (ctx->search_type) {
472 case SEARCH_TYPE_BY_ID: {
473 const uacpi_table_identifiers *id = ctx->id;
474
475 if (id->signature.id != tbl->signature.id)
476 return UACPI_TABLE_ITERATION_DECISION_CONTINUE;
477
478 if (id->oemid[0] != '\0' &&
479 uacpi_memcmp(lhs: id->oemid, rhs: tbl->hdr->oemid, count: sizeof(id->oemid)) != 0)
480 return UACPI_TABLE_ITERATION_DECISION_CONTINUE;
481
482 if (id->oem_table_id[0] != '\0' &&
483 uacpi_memcmp(lhs: id->oem_table_id, rhs: tbl->hdr->oem_table_id,
484 count: sizeof(id->oem_table_id)) != 0)
485 return UACPI_TABLE_ITERATION_DECISION_CONTINUE;
486
487 break;
488 }
489
490 case SEARCH_TYPE_MATCH:
491 if (!ctx->match_cb(tbl))
492 return UACPI_TABLE_ITERATION_DECISION_CONTINUE;
493
494 }
495
496 out_table = ctx->out_table;
497 out_table->ptr = tbl->ptr;
498 out_table->index = idx;
499 ctx->found = UACPI_TRUE;
500
501 return UACPI_TABLE_ITERATION_DECISION_BREAK;
502}
503
504uacpi_status uacpi_table_match(
505 uacpi_size base_idx, uacpi_table_match_callback cb, uacpi_table *out_table
506)
507{
508 struct table_search_ctx ctx = {
509 .match_cb = cb,
510 .search_type = SEARCH_TYPE_MATCH,
511 .out_table = out_table,
512 };
513
514 uacpi_for_each_table(base_idx, cb: do_search_tables, user: &ctx);
515 if (!ctx.found)
516 return UACPI_STATUS_NOT_FOUND;
517
518 return UACPI_STATUS_OK;
519}
520
521
522static uacpi_status find_table(
523 uacpi_size base_idx, const uacpi_table_identifiers *id,
524 uacpi_table *out_table
525)
526{
527 struct table_search_ctx ctx = {
528 .id = id,
529 .out_table = out_table,
530 .search_type = SEARCH_TYPE_BY_ID,
531 };
532
533 uacpi_for_each_table(base_idx, cb: do_search_tables, user: &ctx);
534 if (!ctx.found)
535 return UACPI_STATUS_NOT_FOUND;
536
537 return UACPI_STATUS_OK;
538}
539
540uacpi_status uacpi_table_find_by_signature(
541 const uacpi_char *signature_string, struct uacpi_table *out_table
542)
543{
544 struct uacpi_table_identifiers id = {
545 .signature = {
546 .text = {
547 signature_string[0],
548 signature_string[1],
549 signature_string[2],
550 signature_string[3]
551 }
552 }
553 };
554
555 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
556 return find_table(base_idx: 0, id: &id, out_table);
557}
558
559uacpi_status
560uacpi_table_find_next_with_same_signature(uacpi_table *in_out_table)
561{
562 struct uacpi_table_identifiers id = { 0 };
563
564 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
565
566 uacpi_memcpy(dest: &id.signature, src: in_out_table->hdr->signature,
567 count: sizeof(id.signature));
568
569 return find_table(base_idx: in_out_table->index + 1, id: &id, out_table: in_out_table);
570}
571
572uacpi_status uacpi_table_find(
573 const uacpi_table_identifiers *id, uacpi_table *out_table
574)
575{
576 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
577 return find_table(base_idx: 0, id, out_table);
578}
579
580static uacpi_status table_ctl(
581 uacpi_size idx, uacpi_u8 expect_set, uacpi_u8 expect_clear,
582 uacpi_u8 set, uacpi_u8 clear, void **out_tbl
583)
584{
585 uacpi_status ret = UACPI_STATUS_OK;
586 struct uacpi_installed_table *tbl;
587
588 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
589
590 UACPI_MUTEX_ACQUIRE(table_mutex);
591 if (uacpi_unlikely(table_array_size(&tables) <= idx)) {
592 uacpi_error(
593 "requested invalid table index %zu (%zu tables installed)\n",
594 idx, table_array_size(&tables)
595 );
596 ret = UACPI_STATUS_INVALID_ARGUMENT;
597 goto out;
598 }
599
600 tbl = table_array_at(arr: &tables, idx);
601 if (uacpi_unlikely((tbl->flags & expect_set) != expect_set)) {
602 uacpi_error(
603 "unexpected table '%.4s' flags %02X, expected %02X to be set\n",
604 tbl->signature.text, tbl->flags, expect_set
605 );
606 ret = UACPI_STATUS_INVALID_ARGUMENT;
607 goto out;
608 }
609
610 if (uacpi_unlikely((tbl->flags & expect_clear) != 0)) {
611 uacpi_error(
612 "unexpected table '%.4s' flags %02X, expected %02X to be clear\n",
613 tbl->signature.text, tbl->flags, expect_clear
614 );
615 ret = UACPI_STATUS_ALREADY_EXISTS;
616 goto out;
617 }
618
619 if (out_tbl != UACPI_NULL)
620 *out_tbl = tbl->ptr;
621
622 tbl->flags |= set;
623 tbl->flags &= ~clear;
624
625out:
626 UACPI_MUTEX_RELEASE(table_mutex);
627 return ret;
628}
629
630uacpi_status uacpi_table_load_with_cause(
631 uacpi_size idx, enum uacpi_table_load_cause cause
632)
633{
634 uacpi_status ret;
635 void *tbl;
636 uacpi_u8 set = UACPI_TABLE_LOADED;
637 uacpi_u8 expect_clear = set;
638
639 ret = table_ctl(idx, expect_set: 0, expect_clear, set, clear: 0, out_tbl: &tbl);
640 if (uacpi_unlikely_error(ret))
641 return ret;
642
643 return uacpi_execute_table(tbl, cause);
644}
645
646uacpi_status uacpi_table_load(uacpi_size idx)
647{
648 return uacpi_table_load_with_cause(idx, cause: UACPI_TABLE_LOAD_CAUSE_HOST);
649}
650
651void uacpi_table_mark_as_loaded(uacpi_size idx)
652{
653 table_ctl(idx, expect_set: 0, expect_clear: 0, UACPI_TABLE_LOADED, clear: 0, UACPI_NULL);
654}
655
656uacpi_u16 fadt_version_sizes[] = {
657 116, 132, 244, 244, 268, 276
658};
659
660static void fadt_ensure_correct_revision(struct acpi_fadt *fadt)
661{
662 uacpi_size current_rev, rev;
663
664 current_rev = fadt->hdr.revision;
665
666 for (rev = 0; rev < UACPI_ARRAY_SIZE(fadt_version_sizes); ++rev) {
667 if (fadt->hdr.length <= fadt_version_sizes[rev])
668 break;
669 }
670
671 if (rev == UACPI_ARRAY_SIZE(fadt_version_sizes)) {
672 uacpi_trace(
673 "FADT revision (%zu) is likely greater than the last "
674 "supported, reducing to %zu\n", current_rev, rev
675 );
676 fadt->hdr.revision = rev;
677 return;
678 }
679
680 rev++;
681
682 if (current_rev != rev && !(rev == 3 && current_rev == 4)) {
683 uacpi_warn(
684 "FADT length %u doesn't match expected for revision %zu, "
685 "assuming version %zu\n", fadt->hdr.length, current_rev,
686 rev
687 );
688 fadt->hdr.revision = rev;
689 }
690}
691
692static void gas_init_system_io(
693 struct acpi_gas *gas, uacpi_u64 address, uacpi_u8 byte_size
694)
695{
696 gas->address = address;
697 gas->address_space_id = UACPI_ADDRESS_SPACE_SYSTEM_IO;
698 gas->register_bit_width = UACPI_MIN(255, byte_size * 8);
699 gas->register_bit_offset = 0;
700 gas->access_size = 0;
701}
702
703
704struct register_description {
705 uacpi_size offset, xoffset;
706 uacpi_size length_offset;
707};
708
709#define fadt_offset(field) uacpi_offsetof(struct acpi_fadt, field)
710
711/*
712 * We convert all the legacy registers into GAS format and write them into
713 * the x_* fields for convenience and faster access at runtime.
714 */
715static struct register_description fadt_registers[] = {
716 {
717 .offset = fadt_offset(pm1a_evt_blk),
718 .xoffset = fadt_offset(x_pm1a_evt_blk),
719 .length_offset = fadt_offset(pm1_evt_len),
720 },
721 {
722 .offset = fadt_offset(pm1b_evt_blk),
723 .xoffset = fadt_offset(x_pm1b_evt_blk),
724 .length_offset = fadt_offset(pm1_evt_len),
725 },
726 {
727 .offset = fadt_offset(pm1a_cnt_blk),
728 .xoffset = fadt_offset(x_pm1a_cnt_blk),
729 .length_offset = fadt_offset(pm1_cnt_len),
730 },
731 {
732 .offset = fadt_offset(pm1b_cnt_blk),
733 .xoffset = fadt_offset(x_pm1b_cnt_blk),
734 .length_offset = fadt_offset(pm1_cnt_len),
735 },
736 {
737 .offset = fadt_offset(pm2_cnt_blk),
738 .xoffset = fadt_offset(x_pm2_cnt_blk),
739 .length_offset = fadt_offset(pm2_cnt_len),
740 },
741 {
742 .offset = fadt_offset(pm_tmr_blk),
743 .xoffset = fadt_offset(x_pm_tmr_blk),
744 .length_offset = fadt_offset(pm_tmr_len),
745 },
746 {
747 .offset = fadt_offset(gpe0_blk),
748 .xoffset = fadt_offset(x_gpe0_blk),
749 .length_offset = fadt_offset(gpe0_blk_len),
750 },
751 {
752 .offset = fadt_offset(gpe1_blk),
753 .xoffset = fadt_offset(x_gpe1_blk),
754 .length_offset = fadt_offset(gpe1_blk_len),
755 },
756};
757
758static void *fadt_relative(uacpi_size offset)
759{
760 return ((uacpi_u8*)&g_uacpi_rt_ctx.fadt) + offset;
761}
762
763static void convert_registers_to_gas(void)
764{
765 uacpi_size i;
766 struct register_description *desc;
767 struct acpi_gas *gas;
768 uacpi_u32 legacy_addr;
769 uacpi_u8 length;
770
771 for (i = 0; i < UACPI_ARRAY_SIZE(fadt_registers); ++i) {
772 desc = &fadt_registers[i];
773
774 legacy_addr = *(uacpi_u32*)fadt_relative(offset: desc->offset);
775 length = *(uacpi_u8*)fadt_relative(offset: desc->length_offset);
776 gas = fadt_relative(offset: desc->xoffset);
777
778 if (gas->address)
779 continue;
780
781 gas_init_system_io(gas, address: legacy_addr, byte_size: length);
782 }
783}
784
785static void split_one_block(
786 struct acpi_gas *src, struct acpi_gas *dst0, struct acpi_gas *dst1
787)
788{
789 uacpi_size byte_length;
790
791 if (src->address == 0)
792 return;
793
794 byte_length = src->register_bit_width / 8;
795 byte_length /= 2;
796
797 gas_init_system_io(gas: dst0, address: src->address, byte_size: byte_length);
798 gas_init_system_io(gas: dst1, address: src->address + byte_length, byte_size: byte_length);
799}
800
801static void split_event_blocks(void)
802{
803 split_one_block(
804 src: &g_uacpi_rt_ctx.fadt.x_pm1a_evt_blk,
805 dst0: &g_uacpi_rt_ctx.pm1a_status_blk,
806 dst1: &g_uacpi_rt_ctx.pm1a_enable_blk
807 );
808 split_one_block(
809 src: &g_uacpi_rt_ctx.fadt.x_pm1b_evt_blk,
810 dst0: &g_uacpi_rt_ctx.pm1b_status_blk,
811 dst1: &g_uacpi_rt_ctx.pm1b_enable_blk
812 );
813}
814
815static uacpi_status initialize_fadt(struct acpi_sdt_hdr *hdr)
816{
817 uacpi_status ret;
818 uacpi_table tbl;
819 struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
820
821 /*
822 * Here we (roughly) follow ACPICA initialization sequence to make sure we
823 * handle potential BIOS quirks with garbage inside FADT correctly.
824 */
825
826 uacpi_memcpy(dest: fadt, src: hdr, UACPI_MIN(sizeof(*fadt), hdr->length));
827
828#ifndef UACPI_REDUCED_HARDWARE
829 g_uacpi_rt_ctx.is_hardware_reduced = fadt->flags & ACPI_HW_REDUCED_ACPI;
830#endif
831
832 fadt_ensure_correct_revision(fadt);
833
834 /*
835 * These are reserved prior to version 3, so zero them out to work around
836 * BIOS implementations that might dirty these.
837 */
838 if (fadt->hdr.revision <= 2) {
839 fadt->preferred_pm_profile = 0;
840 fadt->pstate_cnt = 0;
841 fadt->cst_cnt = 0;
842 fadt->iapc_boot_arch = 0;
843 }
844
845 if (!fadt->x_dsdt)
846 fadt->x_dsdt = fadt->dsdt;
847
848 if (fadt->x_dsdt) {
849 ret = table_install_physical_with_origin_unlocked(
850 phys: fadt->x_dsdt, origin: UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL,
851 ACPI_DSDT_SIGNATURE, out_table: &tbl
852 );
853 if (uacpi_unlikely_error(ret))
854 return ret;
855 }
856
857 if (!uacpi_is_hardware_reduced()) {
858 convert_registers_to_gas();
859 split_event_blocks();
860
861 /*
862 * Unconditionally use 32 bit FACS if it exists, as 64 bit FACS is known
863 * to cause issues on some firmware:
864 * https://bugzilla.kernel.org/show_bug.cgi?id=74021
865 */
866 if (fadt->firmware_ctrl)
867 fadt->x_firmware_ctrl = fadt->firmware_ctrl;
868
869 if (fadt->x_firmware_ctrl) {
870 ret = table_install_physical_with_origin_unlocked(
871 phys: fadt->x_firmware_ctrl, origin: UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL,
872 ACPI_FACS_SIGNATURE, out_table: &tbl
873 );
874 if (uacpi_unlikely_error(ret))
875 return ret;
876
877 g_uacpi_rt_ctx.facs = tbl.ptr;
878 }
879 }
880
881 return UACPI_STATUS_OK;
882}
883
884uacpi_status uacpi_table_fadt(struct acpi_fadt **out_fadt)
885{
886 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
887
888 *out_fadt = &g_uacpi_rt_ctx.fadt;
889 return UACPI_STATUS_OK;
890}
891