162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: tbinstal - ACPI table installation and removal
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("tbinstal")
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*******************************************************************************
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * FUNCTION:    acpi_tb_install_table_with_override
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * PARAMETERS:  new_table_desc          - New table descriptor to install
2262306a36Sopenharmony_ci *              override                - Whether override should be performed
2362306a36Sopenharmony_ci *              table_index             - Where the table index is returned
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * RETURN:      None
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * DESCRIPTION: Install an ACPI table into the global data structure. The
2862306a36Sopenharmony_ci *              table override mechanism is called to allow the host
2962306a36Sopenharmony_ci *              OS to replace any table before it is installed in the root
3062306a36Sopenharmony_ci *              table array.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci ******************************************************************************/
3362306a36Sopenharmony_civoid
3462306a36Sopenharmony_ciacpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
3562306a36Sopenharmony_ci				    u8 override, u32 *table_index)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	u32 i;
3862306a36Sopenharmony_ci	acpi_status status;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	status = acpi_tb_get_next_table_descriptor(&i, NULL);
4162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
4262306a36Sopenharmony_ci		return;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * ACPI Table Override:
4762306a36Sopenharmony_ci	 *
4862306a36Sopenharmony_ci	 * Before we install the table, let the host OS override it with a new
4962306a36Sopenharmony_ci	 * one if desired. Any table within the RSDT/XSDT can be replaced,
5062306a36Sopenharmony_ci	 * including the DSDT which is pointed to by the FADT.
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	if (override) {
5362306a36Sopenharmony_ci		acpi_tb_override_table(new_table_desc);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.tables[i],
5762306a36Sopenharmony_ci				      new_table_desc->address,
5862306a36Sopenharmony_ci				      new_table_desc->flags,
5962306a36Sopenharmony_ci				      new_table_desc->pointer);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	acpi_tb_print_table_header(new_table_desc->address,
6262306a36Sopenharmony_ci				   new_table_desc->pointer);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* This synchronizes acpi_gbl_dsdt_index */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	*table_index = i;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Set the global integer width (based upon revision of the DSDT) */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (i == acpi_gbl_dsdt_index) {
7162306a36Sopenharmony_ci		acpi_ut_set_integer_width(new_table_desc->pointer->revision);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*******************************************************************************
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * FUNCTION:    acpi_tb_install_standard_table
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * PARAMETERS:  address             - Address of the table (might be a virtual
8062306a36Sopenharmony_ci *                                    address depending on the table_flags)
8162306a36Sopenharmony_ci *              flags               - Flags for the table
8262306a36Sopenharmony_ci *              table               - Pointer to the table (required for virtual
8362306a36Sopenharmony_ci *                                    origins, optional for physical)
8462306a36Sopenharmony_ci *              reload              - Whether reload should be performed
8562306a36Sopenharmony_ci *              override            - Whether override should be performed
8662306a36Sopenharmony_ci *              table_index         - Where the table index is returned
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * RETURN:      Status
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * DESCRIPTION: This function is called to verify and install an ACPI table.
9162306a36Sopenharmony_ci *              When this function is called by "Load" or "LoadTable" opcodes,
9262306a36Sopenharmony_ci *              or by acpi_load_table() API, the "Reload" parameter is set.
9362306a36Sopenharmony_ci *              After successfully returning from this function, table is
9462306a36Sopenharmony_ci *              "INSTALLED" but not "VALIDATED".
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci ******************************************************************************/
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciacpi_status
9962306a36Sopenharmony_ciacpi_tb_install_standard_table(acpi_physical_address address,
10062306a36Sopenharmony_ci			       u8 flags,
10162306a36Sopenharmony_ci			       struct acpi_table_header *table,
10262306a36Sopenharmony_ci			       u8 reload, u8 override, u32 *table_index)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u32 i;
10562306a36Sopenharmony_ci	acpi_status status = AE_OK;
10662306a36Sopenharmony_ci	struct acpi_table_desc new_table_desc;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(tb_install_standard_table);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Acquire a temporary table descriptor for validation */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	status =
11362306a36Sopenharmony_ci	    acpi_tb_acquire_temp_table(&new_table_desc, address, flags, table);
11462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
11562306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
11662306a36Sopenharmony_ci			    "Could not acquire table length at %8.8X%8.8X",
11762306a36Sopenharmony_ci			    ACPI_FORMAT_UINT64(address)));
11862306a36Sopenharmony_ci		return_ACPI_STATUS(status);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * Optionally do not load any SSDTs from the RSDT/XSDT. This can
12362306a36Sopenharmony_ci	 * be useful for debugging ACPI problems on some machines.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (!reload &&
12662306a36Sopenharmony_ci	    acpi_gbl_disable_ssdt_table_install &&
12762306a36Sopenharmony_ci	    ACPI_COMPARE_NAMESEG(&new_table_desc.signature, ACPI_SIG_SSDT)) {
12862306a36Sopenharmony_ci		ACPI_INFO(("Ignoring installation of %4.4s at %8.8X%8.8X",
12962306a36Sopenharmony_ci			   new_table_desc.signature.ascii,
13062306a36Sopenharmony_ci			   ACPI_FORMAT_UINT64(address)));
13162306a36Sopenharmony_ci		goto release_and_exit;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Acquire the table lock */
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Validate and verify a table before installation */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	status = acpi_tb_verify_temp_table(&new_table_desc, NULL, &i);
14162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
14262306a36Sopenharmony_ci		if (status == AE_CTRL_TERMINATE) {
14362306a36Sopenharmony_ci			/*
14462306a36Sopenharmony_ci			 * Table was unloaded, allow it to be reloaded.
14562306a36Sopenharmony_ci			 * As we are going to return AE_OK to the caller, we should
14662306a36Sopenharmony_ci			 * take the responsibility of freeing the input descriptor.
14762306a36Sopenharmony_ci			 * Refill the input descriptor to ensure
14862306a36Sopenharmony_ci			 * acpi_tb_install_table_with_override() can be called again to
14962306a36Sopenharmony_ci			 * indicate the re-installation.
15062306a36Sopenharmony_ci			 */
15162306a36Sopenharmony_ci			acpi_tb_uninstall_table(&new_table_desc);
15262306a36Sopenharmony_ci			(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
15362306a36Sopenharmony_ci			*table_index = i;
15462306a36Sopenharmony_ci			return_ACPI_STATUS(AE_OK);
15562306a36Sopenharmony_ci		}
15662306a36Sopenharmony_ci		goto unlock_and_exit;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Add the table to the global root table list */
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	acpi_tb_install_table_with_override(&new_table_desc, override,
16262306a36Sopenharmony_ci					    table_index);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Invoke table handler */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
16762306a36Sopenharmony_ci	acpi_tb_notify_table(ACPI_TABLE_EVENT_INSTALL, new_table_desc.pointer);
16862306a36Sopenharmony_ci	(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciunlock_and_exit:
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Release the table lock */
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cirelease_and_exit:
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Release the temporary table descriptor */
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	acpi_tb_release_temp_table(&new_table_desc);
18162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/*******************************************************************************
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * FUNCTION:    acpi_tb_override_table
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * PARAMETERS:  old_table_desc      - Validated table descriptor to be
18962306a36Sopenharmony_ci *                                    overridden
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * RETURN:      None
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * DESCRIPTION: Attempt table override by calling the OSL override functions.
19462306a36Sopenharmony_ci *              Note: If the table is overridden, then the entire new table
19562306a36Sopenharmony_ci *              is acquired and returned by this function.
19662306a36Sopenharmony_ci *              Before/after invocation, the table descriptor is in a state
19762306a36Sopenharmony_ci *              that is "VALIDATED".
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci ******************************************************************************/
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_civoid acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	acpi_status status;
20462306a36Sopenharmony_ci	struct acpi_table_desc new_table_desc;
20562306a36Sopenharmony_ci	struct acpi_table_header *table;
20662306a36Sopenharmony_ci	acpi_physical_address address;
20762306a36Sopenharmony_ci	u32 length;
20862306a36Sopenharmony_ci	ACPI_ERROR_ONLY(char *override_type);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* (1) Attempt logical override (returns a logical address) */
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	status = acpi_os_table_override(old_table_desc->pointer, &table);
21362306a36Sopenharmony_ci	if (ACPI_SUCCESS(status) && table) {
21462306a36Sopenharmony_ci		acpi_tb_acquire_temp_table(&new_table_desc,
21562306a36Sopenharmony_ci					   ACPI_PTR_TO_PHYSADDR(table),
21662306a36Sopenharmony_ci					   ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
21762306a36Sopenharmony_ci					   table);
21862306a36Sopenharmony_ci		ACPI_ERROR_ONLY(override_type = "Logical");
21962306a36Sopenharmony_ci		goto finish_override;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* (2) Attempt physical override (returns a physical address) */
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	status = acpi_os_physical_table_override(old_table_desc->pointer,
22562306a36Sopenharmony_ci						 &address, &length);
22662306a36Sopenharmony_ci	if (ACPI_SUCCESS(status) && address && length) {
22762306a36Sopenharmony_ci		acpi_tb_acquire_temp_table(&new_table_desc, address,
22862306a36Sopenharmony_ci					   ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
22962306a36Sopenharmony_ci					   NULL);
23062306a36Sopenharmony_ci		ACPI_ERROR_ONLY(override_type = "Physical");
23162306a36Sopenharmony_ci		goto finish_override;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return;			/* There was no override */
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cifinish_override:
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * Validate and verify a table before overriding, no nested table
24062306a36Sopenharmony_ci	 * duplication check as it's too complicated and unnecessary.
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	status = acpi_tb_verify_temp_table(&new_table_desc, NULL, NULL);
24362306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ACPI_INFO(("%4.4s 0x%8.8X%8.8X"
24862306a36Sopenharmony_ci		   " %s table override, new table: 0x%8.8X%8.8X",
24962306a36Sopenharmony_ci		   old_table_desc->signature.ascii,
25062306a36Sopenharmony_ci		   ACPI_FORMAT_UINT64(old_table_desc->address),
25162306a36Sopenharmony_ci		   override_type, ACPI_FORMAT_UINT64(new_table_desc.address)));
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* We can now uninstall the original table */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	acpi_tb_uninstall_table(old_table_desc);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * Replace the original table descriptor and keep its state as
25962306a36Sopenharmony_ci	 * "VALIDATED".
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	acpi_tb_init_table_descriptor(old_table_desc, new_table_desc.address,
26262306a36Sopenharmony_ci				      new_table_desc.flags,
26362306a36Sopenharmony_ci				      new_table_desc.pointer);
26462306a36Sopenharmony_ci	acpi_tb_validate_temp_table(old_table_desc);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Release the temporary table descriptor */
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	acpi_tb_release_temp_table(&new_table_desc);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*******************************************************************************
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * FUNCTION:    acpi_tb_uninstall_table
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * PARAMETERS:  table_desc          - Table descriptor
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci * RETURN:      None
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * DESCRIPTION: Delete one internal ACPI table
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci ******************************************************************************/
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_civoid acpi_tb_uninstall_table(struct acpi_table_desc *table_desc)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(tb_uninstall_table);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Table must be installed */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (!table_desc->address) {
29162306a36Sopenharmony_ci		return_VOID;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	acpi_tb_invalidate_table(table_desc);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) ==
29762306a36Sopenharmony_ci	    ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) {
29862306a36Sopenharmony_ci		ACPI_FREE(table_desc->pointer);
29962306a36Sopenharmony_ci		table_desc->pointer = NULL;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL);
30362306a36Sopenharmony_ci	return_VOID;
30462306a36Sopenharmony_ci}
305