Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #define BOOT_CTYPE_H
0003 #include "misc.h"
0004 #include "error.h"
0005 #include "../string.h"
0006 #include "efi.h"
0007 
0008 #include <linux/numa.h>
0009 
0010 /*
0011  * Longest parameter of 'acpi=' is 'copy_dsdt', plus an extra '\0'
0012  * for termination.
0013  */
0014 #define MAX_ACPI_ARG_LENGTH 10
0015 
0016 /*
0017  * Immovable memory regions representation. Max amount of memory regions is
0018  * MAX_NUMNODES*2.
0019  */
0020 struct mem_vector immovable_mem[MAX_NUMNODES*2];
0021 
0022 static acpi_physical_address
0023 __efi_get_rsdp_addr(unsigned long cfg_tbl_pa, unsigned int cfg_tbl_len)
0024 {
0025 #ifdef CONFIG_EFI
0026     unsigned long rsdp_addr;
0027     int ret;
0028 
0029     /*
0030      * Search EFI system tables for RSDP. Preferred is ACPI_20_TABLE_GUID to
0031      * ACPI_TABLE_GUID because it has more features.
0032      */
0033     rsdp_addr = efi_find_vendor_table(boot_params, cfg_tbl_pa, cfg_tbl_len,
0034                       ACPI_20_TABLE_GUID);
0035     if (rsdp_addr)
0036         return (acpi_physical_address)rsdp_addr;
0037 
0038     /* No ACPI_20_TABLE_GUID found, fallback to ACPI_TABLE_GUID. */
0039     rsdp_addr = efi_find_vendor_table(boot_params, cfg_tbl_pa, cfg_tbl_len,
0040                       ACPI_TABLE_GUID);
0041     if (rsdp_addr)
0042         return (acpi_physical_address)rsdp_addr;
0043 
0044     debug_putstr("Error getting RSDP address.\n");
0045 #endif
0046     return 0;
0047 }
0048 
0049 static acpi_physical_address efi_get_rsdp_addr(void)
0050 {
0051 #ifdef CONFIG_EFI
0052     unsigned long cfg_tbl_pa = 0;
0053     unsigned int cfg_tbl_len;
0054     unsigned long systab_pa;
0055     unsigned int nr_tables;
0056     enum efi_type et;
0057     int ret;
0058 
0059     et = efi_get_type(boot_params);
0060     if (et == EFI_TYPE_NONE)
0061         return 0;
0062 
0063     systab_pa = efi_get_system_table(boot_params);
0064     if (!systab_pa)
0065         error("EFI support advertised, but unable to locate system table.");
0066 
0067     ret = efi_get_conf_table(boot_params, &cfg_tbl_pa, &cfg_tbl_len);
0068     if (ret || !cfg_tbl_pa)
0069         error("EFI config table not found.");
0070 
0071     return __efi_get_rsdp_addr(cfg_tbl_pa, cfg_tbl_len);
0072 #else
0073     return 0;
0074 #endif
0075 }
0076 
0077 static u8 compute_checksum(u8 *buffer, u32 length)
0078 {
0079     u8 *end = buffer + length;
0080     u8 sum = 0;
0081 
0082     while (buffer < end)
0083         sum += *(buffer++);
0084 
0085     return sum;
0086 }
0087 
0088 /* Search a block of memory for the RSDP signature. */
0089 static u8 *scan_mem_for_rsdp(u8 *start, u32 length)
0090 {
0091     struct acpi_table_rsdp *rsdp;
0092     u8 *address, *end;
0093 
0094     end = start + length;
0095 
0096     /* Search from given start address for the requested length */
0097     for (address = start; address < end; address += ACPI_RSDP_SCAN_STEP) {
0098         /*
0099          * Both RSDP signature and checksum must be correct.
0100          * Note: Sometimes there exists more than one RSDP in memory;
0101          * the valid RSDP has a valid checksum, all others have an
0102          * invalid checksum.
0103          */
0104         rsdp = (struct acpi_table_rsdp *)address;
0105 
0106         /* BAD Signature */
0107         if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature))
0108             continue;
0109 
0110         /* Check the standard checksum */
0111         if (compute_checksum((u8 *)rsdp, ACPI_RSDP_CHECKSUM_LENGTH))
0112             continue;
0113 
0114         /* Check extended checksum if table version >= 2 */
0115         if ((rsdp->revision >= 2) &&
0116             (compute_checksum((u8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH)))
0117             continue;
0118 
0119         /* Signature and checksum valid, we have found a real RSDP */
0120         return address;
0121     }
0122     return NULL;
0123 }
0124 
0125 /* Search RSDP address in EBDA. */
0126 static acpi_physical_address bios_get_rsdp_addr(void)
0127 {
0128     unsigned long address;
0129     u8 *rsdp;
0130 
0131     /* Get the location of the Extended BIOS Data Area (EBDA) */
0132     address = *(u16 *)ACPI_EBDA_PTR_LOCATION;
0133     address <<= 4;
0134 
0135     /*
0136      * Search EBDA paragraphs (EBDA is required to be a minimum of
0137      * 1K length)
0138      */
0139     if (address > 0x400) {
0140         rsdp = scan_mem_for_rsdp((u8 *)address, ACPI_EBDA_WINDOW_SIZE);
0141         if (rsdp)
0142             return (acpi_physical_address)(unsigned long)rsdp;
0143     }
0144 
0145     /* Search upper memory: 16-byte boundaries in E0000h-FFFFFh */
0146     rsdp = scan_mem_for_rsdp((u8 *) ACPI_HI_RSDP_WINDOW_BASE,
0147                     ACPI_HI_RSDP_WINDOW_SIZE);
0148     if (rsdp)
0149         return (acpi_physical_address)(unsigned long)rsdp;
0150 
0151     return 0;
0152 }
0153 
0154 /* Return RSDP address on success, otherwise 0. */
0155 acpi_physical_address get_rsdp_addr(void)
0156 {
0157     acpi_physical_address pa;
0158 
0159     pa = boot_params->acpi_rsdp_addr;
0160 
0161     if (!pa)
0162         pa = efi_get_rsdp_addr();
0163 
0164     if (!pa)
0165         pa = bios_get_rsdp_addr();
0166 
0167     return pa;
0168 }
0169 
0170 #if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_MEMORY_HOTREMOVE)
0171 /*
0172  * Max length of 64-bit hex address string is 19, prefix "0x" + 16 hex
0173  * digits, and '\0' for termination.
0174  */
0175 #define MAX_ADDR_LEN 19
0176 
0177 static unsigned long get_cmdline_acpi_rsdp(void)
0178 {
0179     unsigned long addr = 0;
0180 
0181 #ifdef CONFIG_KEXEC
0182     char val[MAX_ADDR_LEN] = { };
0183     int ret;
0184 
0185     ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN);
0186     if (ret < 0)
0187         return 0;
0188 
0189     if (boot_kstrtoul(val, 16, &addr))
0190         return 0;
0191 #endif
0192     return addr;
0193 }
0194 
0195 /* Compute SRAT address from RSDP. */
0196 static unsigned long get_acpi_srat_table(void)
0197 {
0198     unsigned long root_table, acpi_table;
0199     struct acpi_table_header *header;
0200     struct acpi_table_rsdp *rsdp;
0201     u32 num_entries, size, len;
0202     char arg[10];
0203     u8 *entry;
0204 
0205     /*
0206      * Check whether we were given an RSDP on the command line. We don't
0207      * stash this in boot params because the kernel itself may have
0208      * different ideas about whether to trust a command-line parameter.
0209      */
0210     rsdp = (struct acpi_table_rsdp *)get_cmdline_acpi_rsdp();
0211     if (!rsdp)
0212         rsdp = (struct acpi_table_rsdp *)(long)
0213             boot_params->acpi_rsdp_addr;
0214 
0215     if (!rsdp)
0216         return 0;
0217 
0218     /* Get ACPI root table from RSDP.*/
0219     if (!(cmdline_find_option("acpi", arg, sizeof(arg)) == 4 &&
0220         !strncmp(arg, "rsdt", 4)) &&
0221         rsdp->xsdt_physical_address &&
0222         rsdp->revision > 1) {
0223         root_table = rsdp->xsdt_physical_address;
0224         size = ACPI_XSDT_ENTRY_SIZE;
0225     } else {
0226         root_table = rsdp->rsdt_physical_address;
0227         size = ACPI_RSDT_ENTRY_SIZE;
0228     }
0229 
0230     if (!root_table)
0231         return 0;
0232 
0233     header = (struct acpi_table_header *)root_table;
0234     len = header->length;
0235     if (len < sizeof(struct acpi_table_header) + size)
0236         return 0;
0237 
0238     num_entries = (len - sizeof(struct acpi_table_header)) / size;
0239     entry = (u8 *)(root_table + sizeof(struct acpi_table_header));
0240 
0241     while (num_entries--) {
0242         if (size == ACPI_RSDT_ENTRY_SIZE)
0243             acpi_table = *(u32 *)entry;
0244         else
0245             acpi_table = *(u64 *)entry;
0246 
0247         if (acpi_table) {
0248             header = (struct acpi_table_header *)acpi_table;
0249 
0250             if (ACPI_COMPARE_NAMESEG(header->signature, ACPI_SIG_SRAT))
0251                 return acpi_table;
0252         }
0253         entry += size;
0254     }
0255     return 0;
0256 }
0257 
0258 /**
0259  * count_immovable_mem_regions - Parse SRAT and cache the immovable
0260  * memory regions into the immovable_mem array.
0261  *
0262  * Return the number of immovable memory regions on success, 0 on failure:
0263  *
0264  * - Too many immovable memory regions
0265  * - ACPI off or no SRAT found
0266  * - No immovable memory region found.
0267  */
0268 int count_immovable_mem_regions(void)
0269 {
0270     unsigned long table_addr, table_end, table;
0271     struct acpi_subtable_header *sub_table;
0272     struct acpi_table_header *table_header;
0273     char arg[MAX_ACPI_ARG_LENGTH];
0274     int num = 0;
0275 
0276     if (cmdline_find_option("acpi", arg, sizeof(arg)) == 3 &&
0277         !strncmp(arg, "off", 3))
0278         return 0;
0279 
0280     table_addr = get_acpi_srat_table();
0281     if (!table_addr)
0282         return 0;
0283 
0284     table_header = (struct acpi_table_header *)table_addr;
0285     table_end = table_addr + table_header->length;
0286     table = table_addr + sizeof(struct acpi_table_srat);
0287 
0288     while (table + sizeof(struct acpi_subtable_header) < table_end) {
0289 
0290         sub_table = (struct acpi_subtable_header *)table;
0291         if (!sub_table->length) {
0292             debug_putstr("Invalid zero length SRAT subtable.\n");
0293             return 0;
0294         }
0295 
0296         if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) {
0297             struct acpi_srat_mem_affinity *ma;
0298 
0299             ma = (struct acpi_srat_mem_affinity *)sub_table;
0300             if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) {
0301                 immovable_mem[num].start = ma->base_address;
0302                 immovable_mem[num].size = ma->length;
0303                 num++;
0304             }
0305 
0306             if (num >= MAX_NUMNODES*2) {
0307                 debug_putstr("Too many immovable memory regions, aborting.\n");
0308                 return 0;
0309             }
0310         }
0311         table += sub_table->length;
0312     }
0313     return num;
0314 }
0315 #endif /* CONFIG_RANDOMIZE_BASE && CONFIG_MEMORY_HOTREMOVE */