| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * acpi_tables.c - ACPI Boot-Time Table Parsing |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
|---|
| 5 | | - * |
|---|
| 6 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | | - * |
|---|
| 13 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | | - * GNU General Public License for more details. |
|---|
| 17 | | - * |
|---|
| 18 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 19 | | - * |
|---|
| 20 | 6 | */ |
|---|
| 21 | 7 | |
|---|
| 22 | 8 | /* Uncomment next line to get verbose printout */ |
|---|
| .. | .. |
|---|
| 31 | 17 | #include <linux/irq.h> |
|---|
| 32 | 18 | #include <linux/errno.h> |
|---|
| 33 | 19 | #include <linux/acpi.h> |
|---|
| 34 | | -#include <linux/bootmem.h> |
|---|
| 35 | | -#include <linux/earlycpio.h> |
|---|
| 36 | 20 | #include <linux/memblock.h> |
|---|
| 21 | +#include <linux/earlycpio.h> |
|---|
| 37 | 22 | #include <linux/initrd.h> |
|---|
| 23 | +#include <linux/security.h> |
|---|
| 38 | 24 | #include "internal.h" |
|---|
| 39 | 25 | |
|---|
| 40 | 26 | #ifdef CONFIG_ACPI_CUSTOM_DSDT |
|---|
| .. | .. |
|---|
| 49 | 35 | static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; |
|---|
| 50 | 36 | |
|---|
| 51 | 37 | static int acpi_apic_instance __initdata; |
|---|
| 38 | + |
|---|
| 39 | +enum acpi_subtable_type { |
|---|
| 40 | + ACPI_SUBTABLE_COMMON, |
|---|
| 41 | + ACPI_SUBTABLE_HMAT, |
|---|
| 42 | +}; |
|---|
| 43 | + |
|---|
| 44 | +struct acpi_subtable_entry { |
|---|
| 45 | + union acpi_subtable_headers *hdr; |
|---|
| 46 | + enum acpi_subtable_type type; |
|---|
| 47 | +}; |
|---|
| 52 | 48 | |
|---|
| 53 | 49 | /* |
|---|
| 54 | 50 | * Disable table checksum verification for the early stage due to the size |
|---|
| .. | .. |
|---|
| 218 | 214 | } |
|---|
| 219 | 215 | } |
|---|
| 220 | 216 | |
|---|
| 217 | +static unsigned long __init |
|---|
| 218 | +acpi_get_entry_type(struct acpi_subtable_entry *entry) |
|---|
| 219 | +{ |
|---|
| 220 | + switch (entry->type) { |
|---|
| 221 | + case ACPI_SUBTABLE_COMMON: |
|---|
| 222 | + return entry->hdr->common.type; |
|---|
| 223 | + case ACPI_SUBTABLE_HMAT: |
|---|
| 224 | + return entry->hdr->hmat.type; |
|---|
| 225 | + } |
|---|
| 226 | + return 0; |
|---|
| 227 | +} |
|---|
| 228 | + |
|---|
| 229 | +static unsigned long __init |
|---|
| 230 | +acpi_get_entry_length(struct acpi_subtable_entry *entry) |
|---|
| 231 | +{ |
|---|
| 232 | + switch (entry->type) { |
|---|
| 233 | + case ACPI_SUBTABLE_COMMON: |
|---|
| 234 | + return entry->hdr->common.length; |
|---|
| 235 | + case ACPI_SUBTABLE_HMAT: |
|---|
| 236 | + return entry->hdr->hmat.length; |
|---|
| 237 | + } |
|---|
| 238 | + return 0; |
|---|
| 239 | +} |
|---|
| 240 | + |
|---|
| 241 | +static unsigned long __init |
|---|
| 242 | +acpi_get_subtable_header_length(struct acpi_subtable_entry *entry) |
|---|
| 243 | +{ |
|---|
| 244 | + switch (entry->type) { |
|---|
| 245 | + case ACPI_SUBTABLE_COMMON: |
|---|
| 246 | + return sizeof(entry->hdr->common); |
|---|
| 247 | + case ACPI_SUBTABLE_HMAT: |
|---|
| 248 | + return sizeof(entry->hdr->hmat); |
|---|
| 249 | + } |
|---|
| 250 | + return 0; |
|---|
| 251 | +} |
|---|
| 252 | + |
|---|
| 253 | +static enum acpi_subtable_type __init |
|---|
| 254 | +acpi_get_subtable_type(char *id) |
|---|
| 255 | +{ |
|---|
| 256 | + if (strncmp(id, ACPI_SIG_HMAT, 4) == 0) |
|---|
| 257 | + return ACPI_SUBTABLE_HMAT; |
|---|
| 258 | + return ACPI_SUBTABLE_COMMON; |
|---|
| 259 | +} |
|---|
| 260 | + |
|---|
| 221 | 261 | /** |
|---|
| 222 | 262 | * acpi_parse_entries_array - for each proc_num find a suitable subtable |
|---|
| 223 | 263 | * |
|---|
| .. | .. |
|---|
| 241 | 281 | * On success returns sum of all matching entries for all proc handlers. |
|---|
| 242 | 282 | * Otherwise, -ENODEV or -EINVAL is returned. |
|---|
| 243 | 283 | */ |
|---|
| 244 | | -static int __init |
|---|
| 245 | | -acpi_parse_entries_array(char *id, unsigned long table_size, |
|---|
| 284 | +static int __init acpi_parse_entries_array(char *id, unsigned long table_size, |
|---|
| 246 | 285 | struct acpi_table_header *table_header, |
|---|
| 247 | 286 | struct acpi_subtable_proc *proc, int proc_num, |
|---|
| 248 | 287 | unsigned int max_entries) |
|---|
| 249 | 288 | { |
|---|
| 250 | | - struct acpi_subtable_header *entry; |
|---|
| 251 | | - unsigned long table_end; |
|---|
| 289 | + struct acpi_subtable_entry entry; |
|---|
| 290 | + unsigned long table_end, subtable_len, entry_len; |
|---|
| 252 | 291 | int count = 0; |
|---|
| 253 | 292 | int errs = 0; |
|---|
| 254 | 293 | int i; |
|---|
| 255 | | - |
|---|
| 256 | | - if (acpi_disabled) |
|---|
| 257 | | - return -ENODEV; |
|---|
| 258 | | - |
|---|
| 259 | | - if (!id) |
|---|
| 260 | | - return -EINVAL; |
|---|
| 261 | | - |
|---|
| 262 | | - if (!table_size) |
|---|
| 263 | | - return -EINVAL; |
|---|
| 264 | | - |
|---|
| 265 | | - if (!table_header) { |
|---|
| 266 | | - pr_warn("%4.4s not present\n", id); |
|---|
| 267 | | - return -ENODEV; |
|---|
| 268 | | - } |
|---|
| 269 | 294 | |
|---|
| 270 | 295 | table_end = (unsigned long)table_header + table_header->length; |
|---|
| 271 | 296 | |
|---|
| 272 | 297 | /* Parse all entries looking for a match. */ |
|---|
| 273 | 298 | |
|---|
| 274 | | - entry = (struct acpi_subtable_header *) |
|---|
| 299 | + entry.type = acpi_get_subtable_type(id); |
|---|
| 300 | + entry.hdr = (union acpi_subtable_headers *) |
|---|
| 275 | 301 | ((unsigned long)table_header + table_size); |
|---|
| 302 | + subtable_len = acpi_get_subtable_header_length(&entry); |
|---|
| 276 | 303 | |
|---|
| 277 | | - while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < |
|---|
| 278 | | - table_end) { |
|---|
| 304 | + while (((unsigned long)entry.hdr) + subtable_len < table_end) { |
|---|
| 279 | 305 | if (max_entries && count >= max_entries) |
|---|
| 280 | 306 | break; |
|---|
| 281 | 307 | |
|---|
| 282 | 308 | for (i = 0; i < proc_num; i++) { |
|---|
| 283 | | - if (entry->type != proc[i].id) |
|---|
| 309 | + if (acpi_get_entry_type(&entry) != proc[i].id) |
|---|
| 284 | 310 | continue; |
|---|
| 285 | 311 | if (!proc[i].handler || |
|---|
| 286 | | - (!errs && proc[i].handler(entry, table_end))) { |
|---|
| 312 | + (!errs && proc[i].handler(entry.hdr, table_end))) { |
|---|
| 287 | 313 | errs++; |
|---|
| 288 | 314 | continue; |
|---|
| 289 | 315 | } |
|---|
| .. | .. |
|---|
| 298 | 324 | * If entry->length is 0, break from this loop to avoid |
|---|
| 299 | 325 | * infinite loop. |
|---|
| 300 | 326 | */ |
|---|
| 301 | | - if (entry->length == 0) { |
|---|
| 327 | + entry_len = acpi_get_entry_length(&entry); |
|---|
| 328 | + if (entry_len == 0) { |
|---|
| 302 | 329 | pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); |
|---|
| 303 | 330 | return -EINVAL; |
|---|
| 304 | 331 | } |
|---|
| 305 | 332 | |
|---|
| 306 | | - entry = (struct acpi_subtable_header *) |
|---|
| 307 | | - ((unsigned long)entry + entry->length); |
|---|
| 333 | + entry.hdr = (union acpi_subtable_headers *) |
|---|
| 334 | + ((unsigned long)entry.hdr + entry_len); |
|---|
| 308 | 335 | } |
|---|
| 309 | 336 | |
|---|
| 310 | 337 | if (max_entries && count > max_entries) { |
|---|
| .. | .. |
|---|
| 315 | 342 | return errs ? -EINVAL : count; |
|---|
| 316 | 343 | } |
|---|
| 317 | 344 | |
|---|
| 318 | | -int __init |
|---|
| 319 | | -acpi_table_parse_entries_array(char *id, |
|---|
| 345 | +int __init acpi_table_parse_entries_array(char *id, |
|---|
| 320 | 346 | unsigned long table_size, |
|---|
| 321 | 347 | struct acpi_subtable_proc *proc, int proc_num, |
|---|
| 322 | 348 | unsigned int max_entries) |
|---|
| .. | .. |
|---|
| 329 | 355 | return -ENODEV; |
|---|
| 330 | 356 | |
|---|
| 331 | 357 | if (!id) |
|---|
| 358 | + return -EINVAL; |
|---|
| 359 | + |
|---|
| 360 | + if (!table_size) |
|---|
| 332 | 361 | return -EINVAL; |
|---|
| 333 | 362 | |
|---|
| 334 | 363 | if (!strncmp(id, ACPI_SIG_MADT, 4)) |
|---|
| .. | .. |
|---|
| 347 | 376 | return count; |
|---|
| 348 | 377 | } |
|---|
| 349 | 378 | |
|---|
| 350 | | -int __init |
|---|
| 351 | | -acpi_table_parse_entries(char *id, |
|---|
| 379 | +int __init acpi_table_parse_entries(char *id, |
|---|
| 352 | 380 | unsigned long table_size, |
|---|
| 353 | 381 | int entry_id, |
|---|
| 354 | 382 | acpi_tbl_entry_handler handler, |
|---|
| .. | .. |
|---|
| 363 | 391 | max_entries); |
|---|
| 364 | 392 | } |
|---|
| 365 | 393 | |
|---|
| 366 | | -int __init |
|---|
| 367 | | -acpi_table_parse_madt(enum acpi_madt_type id, |
|---|
| 394 | +int __init acpi_table_parse_madt(enum acpi_madt_type id, |
|---|
| 368 | 395 | acpi_tbl_entry_handler handler, unsigned int max_entries) |
|---|
| 369 | 396 | { |
|---|
| 370 | 397 | return acpi_table_parse_entries(ACPI_SIG_MADT, |
|---|
| .. | .. |
|---|
| 452 | 479 | } |
|---|
| 453 | 480 | |
|---|
| 454 | 481 | /* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */ |
|---|
| 455 | | -static const char * const table_sigs[] = { |
|---|
| 456 | | - ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ, |
|---|
| 457 | | - ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT, |
|---|
| 458 | | - ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF, |
|---|
| 459 | | - ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET, |
|---|
| 460 | | - ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI, |
|---|
| 461 | | - ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA, |
|---|
| 462 | | - ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT, |
|---|
| 463 | | - ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, |
|---|
| 464 | | - ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, |
|---|
| 465 | | - ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, NULL }; |
|---|
| 482 | +static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = { |
|---|
| 483 | + ACPI_SIG_BERT, ACPI_SIG_BGRT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, |
|---|
| 484 | + ACPI_SIG_EINJ, ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, |
|---|
| 485 | + ACPI_SIG_MSCT, ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, |
|---|
| 486 | + ACPI_SIG_ASF, ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, |
|---|
| 487 | + ACPI_SIG_HPET, ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, |
|---|
| 488 | + ACPI_SIG_MCHI, ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, |
|---|
| 489 | + ACPI_SIG_TCPA, ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, |
|---|
| 490 | + ACPI_SIG_WDDT, ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, |
|---|
| 491 | + ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, |
|---|
| 492 | + ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, |
|---|
| 493 | + ACPI_SIG_NHLT }; |
|---|
| 466 | 494 | |
|---|
| 467 | 495 | #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) |
|---|
| 468 | 496 | |
|---|
| .. | .. |
|---|
| 474 | 502 | |
|---|
| 475 | 503 | void __init acpi_table_upgrade(void) |
|---|
| 476 | 504 | { |
|---|
| 477 | | - void *data = (void *)initrd_start; |
|---|
| 478 | | - size_t size = initrd_end - initrd_start; |
|---|
| 505 | + void *data; |
|---|
| 506 | + size_t size; |
|---|
| 479 | 507 | int sig, no, table_nr = 0, total_offset = 0; |
|---|
| 480 | 508 | long offset = 0; |
|---|
| 481 | 509 | struct acpi_table_header *table; |
|---|
| 482 | 510 | char cpio_path[32] = "kernel/firmware/acpi/"; |
|---|
| 483 | 511 | struct cpio_data file; |
|---|
| 512 | + |
|---|
| 513 | + if (IS_ENABLED(CONFIG_ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD)) { |
|---|
| 514 | + data = __initramfs_start; |
|---|
| 515 | + size = __initramfs_size; |
|---|
| 516 | + } else { |
|---|
| 517 | + data = (void *)initrd_start; |
|---|
| 518 | + size = initrd_end - initrd_start; |
|---|
| 519 | + } |
|---|
| 484 | 520 | |
|---|
| 485 | 521 | if (data == NULL || size == 0) |
|---|
| 486 | 522 | return; |
|---|
| .. | .. |
|---|
| 501 | 537 | |
|---|
| 502 | 538 | table = file.data; |
|---|
| 503 | 539 | |
|---|
| 504 | | - for (sig = 0; table_sigs[sig]; sig++) |
|---|
| 540 | + for (sig = 0; sig < ARRAY_SIZE(table_sigs); sig++) |
|---|
| 505 | 541 | if (!memcmp(table->signature, table_sigs[sig], 4)) |
|---|
| 506 | 542 | break; |
|---|
| 507 | 543 | |
|---|
| 508 | | - if (!table_sigs[sig]) { |
|---|
| 544 | + if (sig >= ARRAY_SIZE(table_sigs)) { |
|---|
| 509 | 545 | pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n", |
|---|
| 510 | 546 | cpio_path, file.name); |
|---|
| 511 | 547 | continue; |
|---|
| .. | .. |
|---|
| 531 | 567 | } |
|---|
| 532 | 568 | if (table_nr == 0) |
|---|
| 533 | 569 | return; |
|---|
| 570 | + |
|---|
| 571 | + if (security_locked_down(LOCKDOWN_ACPI_TABLES)) { |
|---|
| 572 | + pr_notice("kernel is locked down, ignoring table override\n"); |
|---|
| 573 | + return; |
|---|
| 574 | + } |
|---|
| 534 | 575 | |
|---|
| 535 | 576 | acpi_tables_addr = |
|---|
| 536 | 577 | memblock_find_in_range(0, ACPI_TABLE_UPGRADE_MAX_PHYS, |
|---|
| .. | .. |
|---|
| 663 | 704 | table_length = table->length; |
|---|
| 664 | 705 | |
|---|
| 665 | 706 | /* Skip RSDT/XSDT which should only be used for override */ |
|---|
| 666 | | - if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) || |
|---|
| 667 | | - ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) { |
|---|
| 707 | + if (ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_RSDT) || |
|---|
| 708 | + ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_XSDT)) { |
|---|
| 668 | 709 | acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); |
|---|
| 669 | 710 | goto next_table; |
|---|
| 670 | 711 | } |
|---|
| .. | .. |
|---|
| 713 | 754 | table_length); |
|---|
| 714 | 755 | } |
|---|
| 715 | 756 | |
|---|
| 716 | | -acpi_status |
|---|
| 717 | | -acpi_os_table_override(struct acpi_table_header *existing_table, |
|---|
| 757 | +#ifdef CONFIG_ACPI_CUSTOM_DSDT |
|---|
| 758 | +static void *amlcode __attribute__ ((weakref("AmlCode"))); |
|---|
| 759 | +static void *dsdt_amlcode __attribute__ ((weakref("dsdt_aml_code"))); |
|---|
| 760 | +#endif |
|---|
| 761 | + |
|---|
| 762 | +acpi_status acpi_os_table_override(struct acpi_table_header *existing_table, |
|---|
| 718 | 763 | struct acpi_table_header **new_table) |
|---|
| 719 | 764 | { |
|---|
| 720 | 765 | if (!existing_table || !new_table) |
|---|
| .. | .. |
|---|
| 723 | 768 | *new_table = NULL; |
|---|
| 724 | 769 | |
|---|
| 725 | 770 | #ifdef CONFIG_ACPI_CUSTOM_DSDT |
|---|
| 726 | | - if (strncmp(existing_table->signature, "DSDT", 4) == 0) |
|---|
| 727 | | - *new_table = (struct acpi_table_header *)AmlCode; |
|---|
| 771 | + if (!strncmp(existing_table->signature, "DSDT", 4)) { |
|---|
| 772 | + *new_table = (struct acpi_table_header *)&amlcode; |
|---|
| 773 | + if (!(*new_table)) |
|---|
| 774 | + *new_table = (struct acpi_table_header *)&dsdt_amlcode; |
|---|
| 775 | + } |
|---|
| 728 | 776 | #endif |
|---|
| 729 | 777 | if (*new_table != NULL) |
|---|
| 730 | 778 | acpi_table_taint(existing_table); |
|---|
| .. | .. |
|---|
| 809 | 857 | |
|---|
| 810 | 858 | return 0; |
|---|
| 811 | 859 | } |
|---|
| 812 | | - |
|---|
| 813 | 860 | early_param("acpi_apic_instance", acpi_parse_apic_instance); |
|---|
| 814 | 861 | |
|---|
| 815 | 862 | static int __init acpi_force_table_verification_setup(char *s) |
|---|
| .. | .. |
|---|
| 818 | 865 | |
|---|
| 819 | 866 | return 0; |
|---|
| 820 | 867 | } |
|---|
| 821 | | - |
|---|
| 822 | 868 | early_param("acpi_force_table_verification", acpi_force_table_verification_setup); |
|---|
| 823 | 869 | |
|---|
| 824 | 870 | static int __init acpi_force_32bit_fadt_addr(char *s) |
|---|
| .. | .. |
|---|
| 828 | 874 | |
|---|
| 829 | 875 | return 0; |
|---|
| 830 | 876 | } |
|---|
| 831 | | - |
|---|
| 832 | 877 | early_param("acpi_force_32bit_fadt_addr", acpi_force_32bit_fadt_addr); |
|---|