13c98e71bSChao Fan // SPDX-License-Identifier: GPL-2.0
23c98e71bSChao Fan #define BOOT_CTYPE_H
33c98e71bSChao Fan #include "misc.h"
43c98e71bSChao Fan #include "error.h"
53c98e71bSChao Fan #include "../string.h"
65dc91f2dSBorislav Petkov #include "efi.h"
73c98e71bSChao Fan
802a3e3cdSChao Fan #include <linux/numa.h>
93c98e71bSChao Fan
103c98e71bSChao Fan /*
1102a3e3cdSChao Fan * Longest parameter of 'acpi=' is 'copy_dsdt', plus an extra '\0'
1202a3e3cdSChao Fan * for termination.
1302a3e3cdSChao Fan */
1402a3e3cdSChao Fan #define MAX_ACPI_ARG_LENGTH 10
1502a3e3cdSChao Fan
1602a3e3cdSChao Fan /*
1702a3e3cdSChao Fan * Immovable memory regions representation. Max amount of memory regions is
1802a3e3cdSChao Fan * MAX_NUMNODES*2.
1902a3e3cdSChao Fan */
2002a3e3cdSChao Fan struct mem_vector immovable_mem[MAX_NUMNODES*2];
2102a3e3cdSChao Fan
220a23ebc6SJunichi Nomura static acpi_physical_address
__efi_get_rsdp_addr(unsigned long cfg_tbl_pa,unsigned int cfg_tbl_len)23dee602ddSMichael Roth __efi_get_rsdp_addr(unsigned long cfg_tbl_pa, unsigned int cfg_tbl_len)
2433f0df8dSChao Fan {
2533f0df8dSChao Fan #ifdef CONFIG_EFI
26dee602ddSMichael Roth unsigned long rsdp_addr;
27dee602ddSMichael Roth int ret;
280a23ebc6SJunichi Nomura
29dee602ddSMichael Roth /*
30dee602ddSMichael Roth * Search EFI system tables for RSDP. Preferred is ACPI_20_TABLE_GUID to
31dee602ddSMichael Roth * ACPI_TABLE_GUID because it has more features.
32dee602ddSMichael Roth */
33*c59843e8SArd Biesheuvel rsdp_addr = efi_find_vendor_table(boot_params_ptr, cfg_tbl_pa, cfg_tbl_len,
34dee602ddSMichael Roth ACPI_20_TABLE_GUID);
35dee602ddSMichael Roth if (rsdp_addr)
36dee602ddSMichael Roth return (acpi_physical_address)rsdp_addr;
370a23ebc6SJunichi Nomura
38dee602ddSMichael Roth /* No ACPI_20_TABLE_GUID found, fallback to ACPI_TABLE_GUID. */
39*c59843e8SArd Biesheuvel rsdp_addr = efi_find_vendor_table(boot_params_ptr, cfg_tbl_pa, cfg_tbl_len,
40dee602ddSMichael Roth ACPI_TABLE_GUID);
41dee602ddSMichael Roth if (rsdp_addr)
42dee602ddSMichael Roth return (acpi_physical_address)rsdp_addr;
430a23ebc6SJunichi Nomura
44dee602ddSMichael Roth debug_putstr("Error getting RSDP address.\n");
450a23ebc6SJunichi Nomura #endif
46dee602ddSMichael Roth return 0;
470a23ebc6SJunichi Nomura }
480a23ebc6SJunichi Nomura
efi_get_rsdp_addr(void)490a23ebc6SJunichi Nomura static acpi_physical_address efi_get_rsdp_addr(void)
500a23ebc6SJunichi Nomura {
510a23ebc6SJunichi Nomura #ifdef CONFIG_EFI
5261c14cedSMichael Roth unsigned long cfg_tbl_pa = 0;
5361c14cedSMichael Roth unsigned int cfg_tbl_len;
5461c14cedSMichael Roth unsigned long systab_pa;
55f9d230e8SBorislav Petkov unsigned int nr_tables;
567c4146e8SMichael Roth enum efi_type et;
5761c14cedSMichael Roth int ret;
5833f0df8dSChao Fan
59*c59843e8SArd Biesheuvel et = efi_get_type(boot_params_ptr);
60dee602ddSMichael Roth if (et == EFI_TYPE_NONE)
6133f0df8dSChao Fan return 0;
6233f0df8dSChao Fan
63*c59843e8SArd Biesheuvel systab_pa = efi_get_system_table(boot_params_ptr);
6458f3e6b7SMichael Roth if (!systab_pa)
6558f3e6b7SMichael Roth error("EFI support advertised, but unable to locate system table.");
6633f0df8dSChao Fan
67*c59843e8SArd Biesheuvel ret = efi_get_conf_table(boot_params_ptr, &cfg_tbl_pa, &cfg_tbl_len);
6861c14cedSMichael Roth if (ret || !cfg_tbl_pa)
6961c14cedSMichael Roth error("EFI config table not found.");
7033f0df8dSChao Fan
71dee602ddSMichael Roth return __efi_get_rsdp_addr(cfg_tbl_pa, cfg_tbl_len);
720a23ebc6SJunichi Nomura #else
7333f0df8dSChao Fan return 0;
7433f0df8dSChao Fan #endif
7533f0df8dSChao Fan }
7693a209aaSChao Fan
compute_checksum(u8 * buffer,u32 length)7793a209aaSChao Fan static u8 compute_checksum(u8 *buffer, u32 length)
7893a209aaSChao Fan {
7993a209aaSChao Fan u8 *end = buffer + length;
8093a209aaSChao Fan u8 sum = 0;
8193a209aaSChao Fan
8293a209aaSChao Fan while (buffer < end)
8393a209aaSChao Fan sum += *(buffer++);
8493a209aaSChao Fan
8593a209aaSChao Fan return sum;
8693a209aaSChao Fan }
8793a209aaSChao Fan
8893a209aaSChao Fan /* Search a block of memory for the RSDP signature. */
scan_mem_for_rsdp(u8 * start,u32 length)8993a209aaSChao Fan static u8 *scan_mem_for_rsdp(u8 *start, u32 length)
9093a209aaSChao Fan {
9193a209aaSChao Fan struct acpi_table_rsdp *rsdp;
9293a209aaSChao Fan u8 *address, *end;
9393a209aaSChao Fan
9493a209aaSChao Fan end = start + length;
9593a209aaSChao Fan
9693a209aaSChao Fan /* Search from given start address for the requested length */
9793a209aaSChao Fan for (address = start; address < end; address += ACPI_RSDP_SCAN_STEP) {
9893a209aaSChao Fan /*
9993a209aaSChao Fan * Both RSDP signature and checksum must be correct.
10093a209aaSChao Fan * Note: Sometimes there exists more than one RSDP in memory;
10193a209aaSChao Fan * the valid RSDP has a valid checksum, all others have an
10293a209aaSChao Fan * invalid checksum.
10393a209aaSChao Fan */
10493a209aaSChao Fan rsdp = (struct acpi_table_rsdp *)address;
10593a209aaSChao Fan
10693a209aaSChao Fan /* BAD Signature */
10793a209aaSChao Fan if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature))
10893a209aaSChao Fan continue;
10993a209aaSChao Fan
11093a209aaSChao Fan /* Check the standard checksum */
11193a209aaSChao Fan if (compute_checksum((u8 *)rsdp, ACPI_RSDP_CHECKSUM_LENGTH))
11293a209aaSChao Fan continue;
11393a209aaSChao Fan
11493a209aaSChao Fan /* Check extended checksum if table version >= 2 */
11593a209aaSChao Fan if ((rsdp->revision >= 2) &&
11693a209aaSChao Fan (compute_checksum((u8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH)))
11793a209aaSChao Fan continue;
11893a209aaSChao Fan
11993a209aaSChao Fan /* Signature and checksum valid, we have found a real RSDP */
12093a209aaSChao Fan return address;
12193a209aaSChao Fan }
12293a209aaSChao Fan return NULL;
12393a209aaSChao Fan }
12493a209aaSChao Fan
12593a209aaSChao Fan /* Search RSDP address in EBDA. */
bios_get_rsdp_addr(void)12693a209aaSChao Fan static acpi_physical_address bios_get_rsdp_addr(void)
12793a209aaSChao Fan {
12893a209aaSChao Fan unsigned long address;
12993a209aaSChao Fan u8 *rsdp;
13093a209aaSChao Fan
13193a209aaSChao Fan /* Get the location of the Extended BIOS Data Area (EBDA) */
13293a209aaSChao Fan address = *(u16 *)ACPI_EBDA_PTR_LOCATION;
13393a209aaSChao Fan address <<= 4;
13493a209aaSChao Fan
13593a209aaSChao Fan /*
13693a209aaSChao Fan * Search EBDA paragraphs (EBDA is required to be a minimum of
13793a209aaSChao Fan * 1K length)
13893a209aaSChao Fan */
13993a209aaSChao Fan if (address > 0x400) {
14093a209aaSChao Fan rsdp = scan_mem_for_rsdp((u8 *)address, ACPI_EBDA_WINDOW_SIZE);
14193a209aaSChao Fan if (rsdp)
14293a209aaSChao Fan return (acpi_physical_address)(unsigned long)rsdp;
14393a209aaSChao Fan }
14493a209aaSChao Fan
14593a209aaSChao Fan /* Search upper memory: 16-byte boundaries in E0000h-FFFFFh */
14693a209aaSChao Fan rsdp = scan_mem_for_rsdp((u8 *) ACPI_HI_RSDP_WINDOW_BASE,
14793a209aaSChao Fan ACPI_HI_RSDP_WINDOW_SIZE);
14893a209aaSChao Fan if (rsdp)
14993a209aaSChao Fan return (acpi_physical_address)(unsigned long)rsdp;
15093a209aaSChao Fan
15193a209aaSChao Fan return 0;
15293a209aaSChao Fan }
1533a63f70bSChao Fan
1543a63f70bSChao Fan /* Return RSDP address on success, otherwise 0. */
get_rsdp_addr(void)1553a63f70bSChao Fan acpi_physical_address get_rsdp_addr(void)
1563a63f70bSChao Fan {
1573a63f70bSChao Fan acpi_physical_address pa;
1583a63f70bSChao Fan
159*c59843e8SArd Biesheuvel pa = boot_params_ptr->acpi_rsdp_addr;
1603a63f70bSChao Fan
1613a63f70bSChao Fan if (!pa)
1623a63f70bSChao Fan pa = efi_get_rsdp_addr();
1633a63f70bSChao Fan
1643a63f70bSChao Fan if (!pa)
1653a63f70bSChao Fan pa = bios_get_rsdp_addr();
1663a63f70bSChao Fan
1673a63f70bSChao Fan return pa;
1683a63f70bSChao Fan }
16902a3e3cdSChao Fan
17002a3e3cdSChao Fan #if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE)
171228d1200SZhenzhong Duan /*
172228d1200SZhenzhong Duan * Max length of 64-bit hex address string is 19, prefix "0x" + 16 hex
173228d1200SZhenzhong Duan * digits, and '\0' for termination.
174228d1200SZhenzhong Duan */
175228d1200SZhenzhong Duan #define MAX_ADDR_LEN 19
176228d1200SZhenzhong Duan
get_cmdline_acpi_rsdp(void)17740ba9309SVamshi K Sthambamkadi static unsigned long get_cmdline_acpi_rsdp(void)
178228d1200SZhenzhong Duan {
17940ba9309SVamshi K Sthambamkadi unsigned long addr = 0;
180228d1200SZhenzhong Duan
181228d1200SZhenzhong Duan #ifdef CONFIG_KEXEC
182228d1200SZhenzhong Duan char val[MAX_ADDR_LEN] = { };
183228d1200SZhenzhong Duan int ret;
184228d1200SZhenzhong Duan
185228d1200SZhenzhong Duan ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN);
186228d1200SZhenzhong Duan if (ret < 0)
187228d1200SZhenzhong Duan return 0;
188228d1200SZhenzhong Duan
18940ba9309SVamshi K Sthambamkadi if (boot_kstrtoul(val, 16, &addr))
190228d1200SZhenzhong Duan return 0;
191228d1200SZhenzhong Duan #endif
192228d1200SZhenzhong Duan return addr;
193228d1200SZhenzhong Duan }
194228d1200SZhenzhong Duan
19502a3e3cdSChao Fan /* Compute SRAT address from RSDP. */
get_acpi_srat_table(void)19602a3e3cdSChao Fan static unsigned long get_acpi_srat_table(void)
19702a3e3cdSChao Fan {
19802a3e3cdSChao Fan unsigned long root_table, acpi_table;
19902a3e3cdSChao Fan struct acpi_table_header *header;
20002a3e3cdSChao Fan struct acpi_table_rsdp *rsdp;
20102a3e3cdSChao Fan u32 num_entries, size, len;
20202a3e3cdSChao Fan char arg[10];
20302a3e3cdSChao Fan u8 *entry;
20402a3e3cdSChao Fan
20541fa1ee9SJosh Boyer /*
20641fa1ee9SJosh Boyer * Check whether we were given an RSDP on the command line. We don't
20741fa1ee9SJosh Boyer * stash this in boot params because the kernel itself may have
20841fa1ee9SJosh Boyer * different ideas about whether to trust a command-line parameter.
20941fa1ee9SJosh Boyer */
21041fa1ee9SJosh Boyer rsdp = (struct acpi_table_rsdp *)get_cmdline_acpi_rsdp();
21141fa1ee9SJosh Boyer if (!rsdp)
21241fa1ee9SJosh Boyer rsdp = (struct acpi_table_rsdp *)(long)
213*c59843e8SArd Biesheuvel boot_params_ptr->acpi_rsdp_addr;
21441fa1ee9SJosh Boyer
21502a3e3cdSChao Fan if (!rsdp)
21602a3e3cdSChao Fan return 0;
21702a3e3cdSChao Fan
21802a3e3cdSChao Fan /* Get ACPI root table from RSDP.*/
21902a3e3cdSChao Fan if (!(cmdline_find_option("acpi", arg, sizeof(arg)) == 4 &&
22002a3e3cdSChao Fan !strncmp(arg, "rsdt", 4)) &&
22102a3e3cdSChao Fan rsdp->xsdt_physical_address &&
22202a3e3cdSChao Fan rsdp->revision > 1) {
22302a3e3cdSChao Fan root_table = rsdp->xsdt_physical_address;
22402a3e3cdSChao Fan size = ACPI_XSDT_ENTRY_SIZE;
22502a3e3cdSChao Fan } else {
22602a3e3cdSChao Fan root_table = rsdp->rsdt_physical_address;
22702a3e3cdSChao Fan size = ACPI_RSDT_ENTRY_SIZE;
22802a3e3cdSChao Fan }
22902a3e3cdSChao Fan
23002a3e3cdSChao Fan if (!root_table)
23102a3e3cdSChao Fan return 0;
23202a3e3cdSChao Fan
23302a3e3cdSChao Fan header = (struct acpi_table_header *)root_table;
23402a3e3cdSChao Fan len = header->length;
23502a3e3cdSChao Fan if (len < sizeof(struct acpi_table_header) + size)
23602a3e3cdSChao Fan return 0;
23702a3e3cdSChao Fan
23802a3e3cdSChao Fan num_entries = (len - sizeof(struct acpi_table_header)) / size;
23902a3e3cdSChao Fan entry = (u8 *)(root_table + sizeof(struct acpi_table_header));
24002a3e3cdSChao Fan
24102a3e3cdSChao Fan while (num_entries--) {
24202a3e3cdSChao Fan if (size == ACPI_RSDT_ENTRY_SIZE)
24302a3e3cdSChao Fan acpi_table = *(u32 *)entry;
24402a3e3cdSChao Fan else
24502a3e3cdSChao Fan acpi_table = *(u64 *)entry;
24602a3e3cdSChao Fan
24702a3e3cdSChao Fan if (acpi_table) {
24802a3e3cdSChao Fan header = (struct acpi_table_header *)acpi_table;
24902a3e3cdSChao Fan
2505599fb69SBob Moore if (ACPI_COMPARE_NAMESEG(header->signature, ACPI_SIG_SRAT))
25102a3e3cdSChao Fan return acpi_table;
25202a3e3cdSChao Fan }
25302a3e3cdSChao Fan entry += size;
25402a3e3cdSChao Fan }
25502a3e3cdSChao Fan return 0;
25602a3e3cdSChao Fan }
25702a3e3cdSChao Fan
25802a3e3cdSChao Fan /**
25902a3e3cdSChao Fan * count_immovable_mem_regions - Parse SRAT and cache the immovable
26002a3e3cdSChao Fan * memory regions into the immovable_mem array.
26102a3e3cdSChao Fan *
26202a3e3cdSChao Fan * Return the number of immovable memory regions on success, 0 on failure:
26302a3e3cdSChao Fan *
26402a3e3cdSChao Fan * - Too many immovable memory regions
26502a3e3cdSChao Fan * - ACPI off or no SRAT found
26602a3e3cdSChao Fan * - No immovable memory region found.
26702a3e3cdSChao Fan */
count_immovable_mem_regions(void)26802a3e3cdSChao Fan int count_immovable_mem_regions(void)
26902a3e3cdSChao Fan {
27002a3e3cdSChao Fan unsigned long table_addr, table_end, table;
27102a3e3cdSChao Fan struct acpi_subtable_header *sub_table;
27202a3e3cdSChao Fan struct acpi_table_header *table_header;
27302a3e3cdSChao Fan char arg[MAX_ACPI_ARG_LENGTH];
27402a3e3cdSChao Fan int num = 0;
27502a3e3cdSChao Fan
27602a3e3cdSChao Fan if (cmdline_find_option("acpi", arg, sizeof(arg)) == 3 &&
27702a3e3cdSChao Fan !strncmp(arg, "off", 3))
27802a3e3cdSChao Fan return 0;
27902a3e3cdSChao Fan
28002a3e3cdSChao Fan table_addr = get_acpi_srat_table();
28102a3e3cdSChao Fan if (!table_addr)
28202a3e3cdSChao Fan return 0;
28302a3e3cdSChao Fan
28402a3e3cdSChao Fan table_header = (struct acpi_table_header *)table_addr;
28502a3e3cdSChao Fan table_end = table_addr + table_header->length;
28602a3e3cdSChao Fan table = table_addr + sizeof(struct acpi_table_srat);
28702a3e3cdSChao Fan
28802a3e3cdSChao Fan while (table + sizeof(struct acpi_subtable_header) < table_end) {
2892b73ea37SSteven Clarkson
29002a3e3cdSChao Fan sub_table = (struct acpi_subtable_header *)table;
2912b73ea37SSteven Clarkson if (!sub_table->length) {
2922b73ea37SSteven Clarkson debug_putstr("Invalid zero length SRAT subtable.\n");
2932b73ea37SSteven Clarkson return 0;
2942b73ea37SSteven Clarkson }
2952b73ea37SSteven Clarkson
29602a3e3cdSChao Fan if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) {
29702a3e3cdSChao Fan struct acpi_srat_mem_affinity *ma;
29802a3e3cdSChao Fan
29902a3e3cdSChao Fan ma = (struct acpi_srat_mem_affinity *)sub_table;
30002a3e3cdSChao Fan if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) {
30102a3e3cdSChao Fan immovable_mem[num].start = ma->base_address;
30202a3e3cdSChao Fan immovable_mem[num].size = ma->length;
30302a3e3cdSChao Fan num++;
30402a3e3cdSChao Fan }
30502a3e3cdSChao Fan
30602a3e3cdSChao Fan if (num >= MAX_NUMNODES*2) {
30702a3e3cdSChao Fan debug_putstr("Too many immovable memory regions, aborting.\n");
30802a3e3cdSChao Fan return 0;
30902a3e3cdSChao Fan }
31002a3e3cdSChao Fan }
31102a3e3cdSChao Fan table += sub_table->length;
31202a3e3cdSChao Fan }
31302a3e3cdSChao Fan return num;
31402a3e3cdSChao Fan }
31502a3e3cdSChao Fan #endif /* CONFIG_RANDOMIZE_BASE && CONFIG_MEMORY_HOTREMOVE */
316