162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: tbxfroot - Find the root ACPI table (RSDT)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <acpi/acpi.h>
1162306a36Sopenharmony_ci#include "accommon.h"
1262306a36Sopenharmony_ci#include "actables.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define _COMPONENT          ACPI_TABLES
1562306a36Sopenharmony_ciACPI_MODULE_NAME("tbxfroot")
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*******************************************************************************
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * FUNCTION:    acpi_tb_get_rsdp_length
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * PARAMETERS:  rsdp                - Pointer to RSDP
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * RETURN:      Table length
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * DESCRIPTION: Get the length of the RSDP
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci ******************************************************************************/
2862306a36Sopenharmony_ciu32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) {
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci		/* BAD Signature */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci		return (0);
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* "Length" field is available if table version >= 2 */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (rsdp->revision >= 2) {
4162306a36Sopenharmony_ci		return (rsdp->length);
4262306a36Sopenharmony_ci	} else {
4362306a36Sopenharmony_ci		return (ACPI_RSDP_CHECKSUM_LENGTH);
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*******************************************************************************
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * FUNCTION:    acpi_tb_validate_rsdp
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * PARAMETERS:  rsdp                - Pointer to unvalidated RSDP
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * RETURN:      Status
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * DESCRIPTION: Validate the RSDP (ptr)
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci ******************************************************************************/
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciacpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/*
6362306a36Sopenharmony_ci	 * The signature and checksum must both be correct
6462306a36Sopenharmony_ci	 *
6562306a36Sopenharmony_ci	 * Note: Sometimes there exists more than one RSDP in memory; the valid
6662306a36Sopenharmony_ci	 * RSDP has a valid checksum, all others have an invalid checksum.
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci	if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) {
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		/* Nope, BAD Signature */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		return (AE_BAD_SIGNATURE);
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Check the standard checksum */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (acpi_ut_checksum((u8 *)rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) {
7862306a36Sopenharmony_ci		return (AE_BAD_CHECKSUM);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* Check extended checksum if table version >= 2 */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if ((rsdp->revision >= 2) &&
8462306a36Sopenharmony_ci	    (acpi_ut_checksum((u8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) {
8562306a36Sopenharmony_ci		return (AE_BAD_CHECKSUM);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return (AE_OK);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*******************************************************************************
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * FUNCTION:    acpi_find_root_pointer
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * PARAMETERS:  table_address           - Where the table pointer is returned
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * RETURN:      Status, RSDP physical address
9862306a36Sopenharmony_ci *
9962306a36Sopenharmony_ci * DESCRIPTION: Search lower 1Mbyte of memory for the root system descriptor
10062306a36Sopenharmony_ci *              pointer structure. If it is found, set *RSDP to point to it.
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * NOTE1:       The RSDP must be either in the first 1K of the Extended
10362306a36Sopenharmony_ci *              BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.)
10462306a36Sopenharmony_ci *              Only a 32-bit physical address is necessary.
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * NOTE2:       This function is always available, regardless of the
10762306a36Sopenharmony_ci *              initialization state of the rest of ACPI.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci ******************************************************************************/
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciacpi_status ACPI_INIT_FUNCTION
11262306a36Sopenharmony_ciacpi_find_root_pointer(acpi_physical_address *table_address)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u8 *table_ptr;
11562306a36Sopenharmony_ci	u8 *mem_rover;
11662306a36Sopenharmony_ci	u32 physical_address;
11762306a36Sopenharmony_ci	u32 ebda_window_size;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_find_root_pointer);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* 1a) Get the location of the Extended BIOS Data Area (EBDA) */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	table_ptr = acpi_os_map_memory((acpi_physical_address)
12462306a36Sopenharmony_ci				       ACPI_EBDA_PTR_LOCATION,
12562306a36Sopenharmony_ci				       ACPI_EBDA_PTR_LENGTH);
12662306a36Sopenharmony_ci	if (!table_ptr) {
12762306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
12862306a36Sopenharmony_ci			    "Could not map memory at 0x%8.8X for length %u",
12962306a36Sopenharmony_ci			    ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ACPI_MOVE_16_TO_32(&physical_address, table_ptr);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Convert segment part to physical address */
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	physical_address <<= 4;
13962306a36Sopenharmony_ci	acpi_os_unmap_memory(table_ptr, ACPI_EBDA_PTR_LENGTH);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* EBDA present? */
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/*
14462306a36Sopenharmony_ci	 * Check that the EBDA pointer from memory is sane and does not point
14562306a36Sopenharmony_ci	 * above valid low memory
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	if (physical_address > 0x400 && physical_address < 0xA0000) {
14862306a36Sopenharmony_ci		/*
14962306a36Sopenharmony_ci		 * Calculate the scan window size
15062306a36Sopenharmony_ci		 * The EBDA is not guaranteed to be larger than a ki_b and in case
15162306a36Sopenharmony_ci		 * that it is smaller, the scanning function would leave the low
15262306a36Sopenharmony_ci		 * memory and continue to the VGA range.
15362306a36Sopenharmony_ci		 */
15462306a36Sopenharmony_ci		ebda_window_size = ACPI_MIN(ACPI_EBDA_WINDOW_SIZE,
15562306a36Sopenharmony_ci					    0xA0000 - physical_address);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		/*
15862306a36Sopenharmony_ci		 * 1b) Search EBDA paragraphs
15962306a36Sopenharmony_ci		 */
16062306a36Sopenharmony_ci		table_ptr = acpi_os_map_memory((acpi_physical_address)
16162306a36Sopenharmony_ci					       physical_address,
16262306a36Sopenharmony_ci					       ebda_window_size);
16362306a36Sopenharmony_ci		if (!table_ptr) {
16462306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO,
16562306a36Sopenharmony_ci				    "Could not map memory at 0x%8.8X for length %u",
16662306a36Sopenharmony_ci				    physical_address, ebda_window_size));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_MEMORY);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		mem_rover =
17262306a36Sopenharmony_ci		    acpi_tb_scan_memory_for_rsdp(table_ptr, ebda_window_size);
17362306a36Sopenharmony_ci		acpi_os_unmap_memory(table_ptr, ebda_window_size);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if (mem_rover) {
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			/* Return the physical address */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci			physical_address +=
18062306a36Sopenharmony_ci			    (u32) ACPI_PTR_DIFF(mem_rover, table_ptr);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci			*table_address =
18362306a36Sopenharmony_ci			    (acpi_physical_address)physical_address;
18462306a36Sopenharmony_ci			return_ACPI_STATUS(AE_OK);
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/*
18962306a36Sopenharmony_ci	 * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	table_ptr = acpi_os_map_memory((acpi_physical_address)
19262306a36Sopenharmony_ci				       ACPI_HI_RSDP_WINDOW_BASE,
19362306a36Sopenharmony_ci				       ACPI_HI_RSDP_WINDOW_SIZE);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (!table_ptr) {
19662306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
19762306a36Sopenharmony_ci			    "Could not map memory at 0x%8.8X for length %u",
19862306a36Sopenharmony_ci			    ACPI_HI_RSDP_WINDOW_BASE,
19962306a36Sopenharmony_ci			    ACPI_HI_RSDP_WINDOW_SIZE));
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	mem_rover =
20562306a36Sopenharmony_ci	    acpi_tb_scan_memory_for_rsdp(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE);
20662306a36Sopenharmony_ci	acpi_os_unmap_memory(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (mem_rover) {
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		/* Return the physical address */
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		physical_address = (u32)
21362306a36Sopenharmony_ci		    (ACPI_HI_RSDP_WINDOW_BASE +
21462306a36Sopenharmony_ci		     ACPI_PTR_DIFF(mem_rover, table_ptr));
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		*table_address = (acpi_physical_address)physical_address;
21762306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* A valid RSDP was not found */
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ACPI_BIOS_ERROR((AE_INFO, "A valid RSDP was not found"));
22362306a36Sopenharmony_ci	return_ACPI_STATUS(AE_NOT_FOUND);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciACPI_EXPORT_SYMBOL_INIT(acpi_find_root_pointer)
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*******************************************************************************
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * FUNCTION:    acpi_tb_scan_memory_for_rsdp
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * PARAMETERS:  start_address       - Starting pointer for search
23362306a36Sopenharmony_ci *              length              - Maximum length to search
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * RETURN:      Pointer to the RSDP if found, otherwise NULL.
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * DESCRIPTION: Search a block of memory for the RSDP signature
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci ******************************************************************************/
24062306a36Sopenharmony_ciu8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	acpi_status status;
24362306a36Sopenharmony_ci	u8 *mem_rover;
24462306a36Sopenharmony_ci	u8 *end_address;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(tb_scan_memory_for_rsdp);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	end_address = start_address + length;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Search from given start address for the requested length */
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (mem_rover = start_address; mem_rover < end_address;
25362306a36Sopenharmony_ci	     mem_rover += ACPI_RSDP_SCAN_STEP) {
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		/* The RSDP signature and checksum must both be correct */
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		status =
25862306a36Sopenharmony_ci		    acpi_tb_validate_rsdp(ACPI_CAST_PTR
25962306a36Sopenharmony_ci					  (struct acpi_table_rsdp, mem_rover));
26062306a36Sopenharmony_ci		if (ACPI_SUCCESS(status)) {
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			/* Sig and checksum valid, we have found a real RSDP */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
26562306a36Sopenharmony_ci					  "RSDP located at physical address %p\n",
26662306a36Sopenharmony_ci					  mem_rover));
26762306a36Sopenharmony_ci			return_PTR(mem_rover);
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		/* No sig match or bad checksum, keep searching */
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Searched entire block, no RSDP was found */
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
27662306a36Sopenharmony_ci			  "Searched entire block from %p, valid RSDP was not found\n",
27762306a36Sopenharmony_ci			  start_address));
27862306a36Sopenharmony_ci	return_PTR(NULL);
27962306a36Sopenharmony_ci}
280