.. | .. |
---|
| 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); |
---|