linux-stable/drivers/acpi/acpica/tbxfload.c
Jessica Clarke 5d6e59665d ACPICA: Use original pointer for virtual origin tables
ACPICA commit dfa3feffa8f760b686207d09dc880cd2f26c72af

Currently the pointer to the table is cast to acpi_physical_address and
later cast back to a pointer to be dereferenced. Whether or not this is
supported is implementation-defined.

On CHERI, and thus Arm's experimental Morello prototype architecture,
pointers are represented as capabilities, which are unforgeable bounded
pointers, providing always-on fine-grained spatial memory safety. This
means that any pointer cast to a plain integer will lose all its
associated metadata, and when cast back to a pointer it will give a
null-derived pointer (one that has the same metadata as null but an
address equal to the integer) that will trap on any dereference. As a
result, this is an implementation where acpi_physical_address cannot be
used as a hack to store real pointers.

Thus, alter the lifecycle of table descriptors. Internal physical tables
keep the current behaviour where only the address is set on install, and
the pointer is set on acquire. Virtual tables (internal and external)
now store the pointer on initialisation and use that on acquire (which
will redundantly set *table_ptr to itself, but changing that is both
unnecessary and overly complicated as acpi_tb_acquire_table is called with
both a pointer to a variable and a pointer to Table->Pointer itself).

This requires propagating the (possible) table pointer everywhere in
order to make sure pointers make it through to acpi_tb_acquire_temp_table,
which requires a change to the acpi_install_table interface. Instead of
taking an ACPI_PHYSADDR_TYPE and a boolean indicating whether it's
physical or virtual, it is now split into acpi_install_table (that takes
an external virtual table pointer) and acpi_install_physical_table (that
takes an ACPI_PHYSADDR_TYPE for an internal physical table address).
This also has the benefit of providing a cleaner API.

Link: https://github.com/acpica/acpica/commit/dfa3feff
Signed-off-by: Bob Moore <robert.moore@intel.com>
[ rjw: Adjust the code in tables.c to match interface changes ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2021-12-27 17:01:28 +01:00

454 lines
13 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: tbxfload - Table load/unload external interfaces
*
* Copyright (C) 2000 - 2021, Intel Corp.
*
*****************************************************************************/
#define EXPORT_ACPI_INTERFACES
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
#include "actables.h"
#include "acevents.h"
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME("tbxfload")
/*******************************************************************************
*
* FUNCTION: acpi_load_tables
*
* PARAMETERS: None
*
* RETURN: Status
*
* DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
*
******************************************************************************/
acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_load_tables);
/*
* Install the default operation region handlers. These are the
* handlers that are defined by the ACPI specification to be
* "always accessible" -- namely, system_memory, system_IO, and
* PCI_Config. This also means that no _REG methods need to be
* run for these address spaces. We need to have these handlers
* installed before any AML code can be executed, especially any
* module-level code (11/2015).
* Note that we allow OSPMs to install their own region handlers
* between acpi_initialize_subsystem() and acpi_load_tables() to use
* their customized default region handlers.
*/
status = acpi_ev_install_region_handlers();
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"During Region initialization"));
return_ACPI_STATUS(status);
}
/* Load the namespace from the tables */
status = acpi_tb_load_namespace();
/* Don't let single failures abort the load */
if (status == AE_CTRL_TERMINATE) {
status = AE_OK;
}
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While loading namespace from ACPI tables"));
}
/*
* Initialize the objects in the namespace that remain uninitialized.
* This runs the executable AML that may be part of the declaration of
* these name objects:
* operation_regions, buffer_fields, Buffers, and Packages.
*
*/
status = acpi_ns_initialize_objects();
if (ACPI_SUCCESS(status)) {
acpi_gbl_namespace_initialized = TRUE;
}
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL_INIT(acpi_load_tables)
/*******************************************************************************
*
* FUNCTION: acpi_tb_load_namespace
*
* PARAMETERS: None
*
* RETURN: Status
*
* DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in
* the RSDT/XSDT.
*
******************************************************************************/
acpi_status acpi_tb_load_namespace(void)
{
acpi_status status;
u32 i;
struct acpi_table_header *new_dsdt;
struct acpi_table_desc *table;
u32 tables_loaded = 0;
u32 tables_failed = 0;
ACPI_FUNCTION_TRACE(tb_load_namespace);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
/*
* Load the namespace. The DSDT is required, but any SSDT and
* PSDT tables are optional. Verify the DSDT.
*/
table = &acpi_gbl_root_table_list.tables[acpi_gbl_dsdt_index];
if (!acpi_gbl_root_table_list.current_table_count ||
!ACPI_COMPARE_NAMESEG(table->signature.ascii, ACPI_SIG_DSDT) ||
ACPI_FAILURE(acpi_tb_validate_table(table))) {
status = AE_NO_ACPI_TABLES;
goto unlock_and_exit;
}
/*
* Save the DSDT pointer for simple access. This is the mapped memory
* address. We must take care here because the address of the .Tables
* array can change dynamically as tables are loaded at run-time. Note:
* .Pointer field is not validated until after call to acpi_tb_validate_table.
*/
acpi_gbl_DSDT = table->pointer;
/*
* Optionally copy the entire DSDT to local memory (instead of simply
* mapping it.) There are some BIOSs that corrupt or replace the original
* DSDT, creating the need for this option. Default is FALSE, do not copy
* the DSDT.
*/
if (acpi_gbl_copy_dsdt_locally) {
new_dsdt = acpi_tb_copy_dsdt(acpi_gbl_dsdt_index);
if (new_dsdt) {
acpi_gbl_DSDT = new_dsdt;
}
}
/*
* Save the original DSDT header for detection of table corruption
* and/or replacement of the DSDT from outside the OS.
*/
memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT,
sizeof(struct acpi_table_header));
/* Load and parse tables */
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed"));
tables_failed++;
} else {
tables_loaded++;
}
/* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
table = &acpi_gbl_root_table_list.tables[i];
if (!table->address ||
(!ACPI_COMPARE_NAMESEG
(table->signature.ascii, ACPI_SIG_SSDT)
&& !ACPI_COMPARE_NAMESEG(table->signature.ascii,
ACPI_SIG_PSDT)
&& !ACPI_COMPARE_NAMESEG(table->signature.ascii,
ACPI_SIG_OSDT))
|| ACPI_FAILURE(acpi_tb_validate_table(table))) {
continue;
}
/* Ignore errors while loading tables, get as many as possible */
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(i, acpi_gbl_root_node);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"(%4.4s:%8.8s) while loading table",
table->signature.ascii,
table->pointer->oem_table_id));
tables_failed++;
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
"Table [%4.4s:%8.8s] (id FF) - Table namespace load failed\n\n",
table->signature.ascii,
table->pointer->oem_table_id));
} else {
tables_loaded++;
}
}
if (!tables_failed) {
ACPI_INFO(("%u ACPI AML tables successfully acquired and loaded", tables_loaded));
} else {
ACPI_ERROR((AE_INFO,
"%u table load failures, %u successful",
tables_failed, tables_loaded));
/* Indicate at least one failure */
status = AE_CTRL_TERMINATE;
}
#ifdef ACPI_APPLICATION
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, "\n"));
#endif
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_install_table
*
* PARAMETERS: table - Pointer to the ACPI table to be installed.
*
* RETURN: Status
*
* DESCRIPTION: Dynamically install an ACPI table.
* Note: This function should only be invoked after
* acpi_initialize_tables() and before acpi_load_tables().
*
******************************************************************************/
acpi_status ACPI_INIT_FUNCTION
acpi_install_table(struct acpi_table_header *table)
{
acpi_status status;
u32 table_index;
ACPI_FUNCTION_TRACE(acpi_install_table);
status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
table, FALSE, FALSE,
&table_index);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL_INIT(acpi_install_table)
/*******************************************************************************
*
* FUNCTION: acpi_install_physical_table
*
* PARAMETERS: address - Address of the ACPI table to be installed.
*
* RETURN: Status
*
* DESCRIPTION: Dynamically install an ACPI table.
* Note: This function should only be invoked after
* acpi_initialize_tables() and before acpi_load_tables().
*
******************************************************************************/
acpi_status ACPI_INIT_FUNCTION
acpi_install_physical_table(acpi_physical_address address)
{
acpi_status status;
u32 table_index;
ACPI_FUNCTION_TRACE(acpi_install_physical_table);
status = acpi_tb_install_standard_table(address,
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
NULL, FALSE, FALSE,
&table_index);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL_INIT(acpi_install_physical_table)
/*******************************************************************************
*
* FUNCTION: acpi_load_table
*
* PARAMETERS: table - Pointer to a buffer containing the ACPI
* table to be loaded.
* table_idx - Pointer to a u32 for storing the table
* index, might be NULL
*
* RETURN: Status
*
* DESCRIPTION: Dynamically load an ACPI table from the caller's buffer. Must
* be a valid ACPI table with a valid ACPI table header.
* Note1: Mainly intended to support hotplug addition of SSDTs.
* Note2: Does not copy the incoming table. User is responsible
* to ensure that the table is not deleted or unmapped.
*
******************************************************************************/
acpi_status acpi_load_table(struct acpi_table_header *table, u32 *table_idx)
{
acpi_status status;
u32 table_index;
ACPI_FUNCTION_TRACE(acpi_load_table);
/* Parameter validation */
if (!table) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/* Install the table and load it into the namespace */
ACPI_INFO(("Host-directed Dynamic ACPI Table Load:"));
status = acpi_tb_install_and_load_table(ACPI_PTR_TO_PHYSADDR(table),
ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
table, FALSE, &table_index);
if (table_idx) {
*table_idx = table_index;
}
if (ACPI_SUCCESS(status)) {
/* Complete the initialization/resolution of new objects */
acpi_ns_initialize_objects();
}
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_load_table)
/*******************************************************************************
*
* FUNCTION: acpi_unload_parent_table
*
* PARAMETERS: object - Handle to any namespace object owned by
* the table to be unloaded
*
* RETURN: Status
*
* DESCRIPTION: Via any namespace object within an SSDT or OEMx table, unloads
* the table and deletes all namespace objects associated with
* that table. Unloading of the DSDT is not allowed.
* Note: Mainly intended to support hotplug removal of SSDTs.
*
******************************************************************************/
acpi_status acpi_unload_parent_table(acpi_handle object)
{
struct acpi_namespace_node *node =
ACPI_CAST_PTR(struct acpi_namespace_node, object);
acpi_status status = AE_NOT_EXIST;
acpi_owner_id owner_id;
u32 i;
ACPI_FUNCTION_TRACE(acpi_unload_parent_table);
/* Parameter validation */
if (!object) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* The node owner_id is currently the same as the parent table ID.
* However, this could change in the future.
*/
owner_id = node->owner_id;
if (!owner_id) {
/* owner_id==0 means DSDT is the owner. DSDT cannot be unloaded */
return_ACPI_STATUS(AE_TYPE);
}
/* Must acquire the table lock during this operation */
status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Find the table in the global table list */
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
if (owner_id != acpi_gbl_root_table_list.tables[i].owner_id) {
continue;
}
/*
* Allow unload of SSDT and OEMx tables only. Do not allow unload
* of the DSDT. No other types of tables should get here, since
* only these types can contain AML and thus are the only types
* that can create namespace objects.
*/
if (ACPI_COMPARE_NAMESEG
(acpi_gbl_root_table_list.tables[i].signature.ascii,
ACPI_SIG_DSDT)) {
status = AE_TYPE;
break;
}
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_tb_unload_table(i);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
break;
}
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_unload_parent_table)
/*******************************************************************************
*
* FUNCTION: acpi_unload_table
*
* PARAMETERS: table_index - Index as returned by acpi_load_table
*
* RETURN: Status
*
* DESCRIPTION: Via the table_index representing an SSDT or OEMx table, unloads
* the table and deletes all namespace objects associated with
* that table. Unloading of the DSDT is not allowed.
* Note: Mainly intended to support hotplug removal of SSDTs.
*
******************************************************************************/
acpi_status acpi_unload_table(u32 table_index)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_unload_table);
if (table_index == 1) {
/* table_index==1 means DSDT is the owner. DSDT cannot be unloaded */
return_ACPI_STATUS(AE_TYPE);
}
status = acpi_tb_unload_table(table_index);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_unload_table)