162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes)
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 "acinterp.h"
1362306a36Sopenharmony_ci#include "acnamesp.h"
1462306a36Sopenharmony_ci#include "actables.h"
1562306a36Sopenharmony_ci#include "acdispat.h"
1662306a36Sopenharmony_ci#include "acevents.h"
1762306a36Sopenharmony_ci#include "amlcode.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define _COMPONENT          ACPI_EXECUTER
2062306a36Sopenharmony_ciACPI_MODULE_NAME("exconfig")
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Local prototypes */
2362306a36Sopenharmony_cistatic acpi_status
2462306a36Sopenharmony_ciacpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic acpi_status
2762306a36Sopenharmony_ciacpi_ex_region_read(union acpi_operand_object *obj_desc,
2862306a36Sopenharmony_ci		    u32 length, u8 *buffer);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*******************************************************************************
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * FUNCTION:    acpi_ex_add_table
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * PARAMETERS:  table               - Pointer to raw table
3562306a36Sopenharmony_ci *              parent_node         - Where to load the table (scope)
3662306a36Sopenharmony_ci *              ddb_handle          - Where to return the table handle.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * RETURN:      Status
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * DESCRIPTION: Common function to Install and Load an ACPI table with a
4162306a36Sopenharmony_ci *              returned table handle.
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci ******************************************************************************/
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic acpi_status
4662306a36Sopenharmony_ciacpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	union acpi_operand_object *obj_desc;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_add_table);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Create an object to be the table handle */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
5562306a36Sopenharmony_ci	if (!obj_desc) {
5662306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Init the table handle */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	obj_desc->common.flags |= AOPOBJ_DATA_VALID;
6262306a36Sopenharmony_ci	obj_desc->reference.class = ACPI_REFCLASS_TABLE;
6362306a36Sopenharmony_ci	obj_desc->reference.value = table_index;
6462306a36Sopenharmony_ci	*ddb_handle = obj_desc;
6562306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*******************************************************************************
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * FUNCTION:    acpi_ex_load_table_op
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * PARAMETERS:  walk_state          - Current state with operands
7362306a36Sopenharmony_ci *              return_desc         - Where to store the return object
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * RETURN:      Status
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * DESCRIPTION: Load an ACPI table from the RSDT/XSDT
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci ******************************************************************************/
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciacpi_status
8262306a36Sopenharmony_ciacpi_ex_load_table_op(struct acpi_walk_state *walk_state,
8362306a36Sopenharmony_ci		      union acpi_operand_object **return_desc)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	acpi_status status;
8662306a36Sopenharmony_ci	union acpi_operand_object **operand = &walk_state->operands[0];
8762306a36Sopenharmony_ci	struct acpi_namespace_node *parent_node;
8862306a36Sopenharmony_ci	struct acpi_namespace_node *start_node;
8962306a36Sopenharmony_ci	struct acpi_namespace_node *parameter_node = NULL;
9062306a36Sopenharmony_ci	union acpi_operand_object *return_obj;
9162306a36Sopenharmony_ci	union acpi_operand_object *ddb_handle;
9262306a36Sopenharmony_ci	u32 table_index;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_load_table_op);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Create the return object */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return_obj = acpi_ut_create_integer_object((u64)0);
9962306a36Sopenharmony_ci	if (!return_obj) {
10062306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	*return_desc = return_obj;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Find the ACPI table in the RSDT/XSDT */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
10862306a36Sopenharmony_ci	status = acpi_tb_find_table(operand[0]->string.pointer,
10962306a36Sopenharmony_ci				    operand[1]->string.pointer,
11062306a36Sopenharmony_ci				    operand[2]->string.pointer, &table_index);
11162306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
11262306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
11362306a36Sopenharmony_ci		if (status != AE_NOT_FOUND) {
11462306a36Sopenharmony_ci			return_ACPI_STATUS(status);
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		/* Table not found, return an Integer=0 and AE_OK */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Default nodes */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	start_node = walk_state->scope_info->scope.node;
12562306a36Sopenharmony_ci	parent_node = acpi_gbl_root_node;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* root_path (optional parameter) */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (operand[3]->string.length > 0) {
13062306a36Sopenharmony_ci		/*
13162306a36Sopenharmony_ci		 * Find the node referenced by the root_path_string. This is the
13262306a36Sopenharmony_ci		 * location within the namespace where the table will be loaded.
13362306a36Sopenharmony_ci		 */
13462306a36Sopenharmony_ci		status = acpi_ns_get_node_unlocked(start_node,
13562306a36Sopenharmony_ci						   operand[3]->string.pointer,
13662306a36Sopenharmony_ci						   ACPI_NS_SEARCH_PARENT,
13762306a36Sopenharmony_ci						   &parent_node);
13862306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
13962306a36Sopenharmony_ci			return_ACPI_STATUS(status);
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* parameter_path (optional parameter) */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (operand[4]->string.length > 0) {
14662306a36Sopenharmony_ci		if ((operand[4]->string.pointer[0] != AML_ROOT_PREFIX) &&
14762306a36Sopenharmony_ci		    (operand[4]->string.pointer[0] != AML_PARENT_PREFIX)) {
14862306a36Sopenharmony_ci			/*
14962306a36Sopenharmony_ci			 * Path is not absolute, so it will be relative to the node
15062306a36Sopenharmony_ci			 * referenced by the root_path_string (or the NS root if omitted)
15162306a36Sopenharmony_ci			 */
15262306a36Sopenharmony_ci			start_node = parent_node;
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		/* Find the node referenced by the parameter_path_string */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		status = acpi_ns_get_node_unlocked(start_node,
15862306a36Sopenharmony_ci						   operand[4]->string.pointer,
15962306a36Sopenharmony_ci						   ACPI_NS_SEARCH_PARENT,
16062306a36Sopenharmony_ci						   &parameter_node);
16162306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
16262306a36Sopenharmony_ci			return_ACPI_STATUS(status);
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Load the table into the namespace */
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ACPI_INFO(("Dynamic OEM Table Load:"));
16962306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
17062306a36Sopenharmony_ci	status = acpi_tb_load_table(table_index, parent_node);
17162306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
17262306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
17362306a36Sopenharmony_ci		return_ACPI_STATUS(status);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	status = acpi_ex_add_table(table_index, &ddb_handle);
17762306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
17862306a36Sopenharmony_ci		return_ACPI_STATUS(status);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Complete the initialization/resolution of new objects */
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
18462306a36Sopenharmony_ci	acpi_ns_initialize_objects();
18562306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Parameter Data (optional) */
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (parameter_node) {
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		/* Store the parameter data into the optional parameter object */
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		status = acpi_ex_store(operand[5],
19462306a36Sopenharmony_ci				       ACPI_CAST_PTR(union acpi_operand_object,
19562306a36Sopenharmony_ci						     parameter_node),
19662306a36Sopenharmony_ci				       walk_state);
19762306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
19862306a36Sopenharmony_ci			(void)acpi_ex_unload_table(ddb_handle);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci			acpi_ut_remove_reference(ddb_handle);
20162306a36Sopenharmony_ci			return_ACPI_STATUS(status);
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Remove the reference to ddb_handle created by acpi_ex_add_table above */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	acpi_ut_remove_reference(ddb_handle);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Return -1 (non-zero) indicates success */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return_obj->integer.value = 0xFFFFFFFFFFFFFFFF;
21262306a36Sopenharmony_ci	return_ACPI_STATUS(status);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*******************************************************************************
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * FUNCTION:    acpi_ex_region_read
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * PARAMETERS:  obj_desc        - Region descriptor
22062306a36Sopenharmony_ci *              length          - Number of bytes to read
22162306a36Sopenharmony_ci *              buffer          - Pointer to where to put the data
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * RETURN:      Status
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * DESCRIPTION: Read data from an operation region. The read starts from the
22662306a36Sopenharmony_ci *              beginning of the region.
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci ******************************************************************************/
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic acpi_status
23162306a36Sopenharmony_ciacpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	acpi_status status;
23462306a36Sopenharmony_ci	u64 value;
23562306a36Sopenharmony_ci	u32 region_offset = 0;
23662306a36Sopenharmony_ci	u32 i;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Bytewise reads */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
24162306a36Sopenharmony_ci		status =
24262306a36Sopenharmony_ci		    acpi_ev_address_space_dispatch(obj_desc, NULL, ACPI_READ,
24362306a36Sopenharmony_ci						   region_offset, 8, &value);
24462306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
24562306a36Sopenharmony_ci			return (status);
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		*buffer = (u8)value;
24962306a36Sopenharmony_ci		buffer++;
25062306a36Sopenharmony_ci		region_offset++;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return (AE_OK);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/*******************************************************************************
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * FUNCTION:    acpi_ex_load_op
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * PARAMETERS:  obj_desc        - Region or Buffer/Field where the table will be
26162306a36Sopenharmony_ci *                                obtained
26262306a36Sopenharmony_ci *              target          - Where the status of the load will be stored
26362306a36Sopenharmony_ci *              walk_state      - Current state
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * RETURN:      Status
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * DESCRIPTION: Load an ACPI table from a field or operation region
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * NOTE: Region Fields (Field, bank_field, index_fields) are resolved to buffer
27062306a36Sopenharmony_ci *       objects before this code is reached.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci *       If source is an operation region, it must refer to system_memory, as
27362306a36Sopenharmony_ci *       per the ACPI specification.
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci ******************************************************************************/
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciacpi_status
27862306a36Sopenharmony_ciacpi_ex_load_op(union acpi_operand_object *obj_desc,
27962306a36Sopenharmony_ci		union acpi_operand_object *target,
28062306a36Sopenharmony_ci		struct acpi_walk_state *walk_state)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	union acpi_operand_object *ddb_handle;
28362306a36Sopenharmony_ci	struct acpi_table_header *table_header;
28462306a36Sopenharmony_ci	struct acpi_table_header *table;
28562306a36Sopenharmony_ci	u32 table_index;
28662306a36Sopenharmony_ci	acpi_status status;
28762306a36Sopenharmony_ci	u32 length;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_load_op);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (target->common.descriptor_type == ACPI_DESC_TYPE_NAMED) {
29262306a36Sopenharmony_ci		target =
29362306a36Sopenharmony_ci		    acpi_ns_get_attached_object(ACPI_CAST_PTR
29462306a36Sopenharmony_ci						(struct acpi_namespace_node,
29562306a36Sopenharmony_ci						 target));
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	if (target->common.type != ACPI_TYPE_INTEGER) {
29862306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Type not integer: %X",
29962306a36Sopenharmony_ci			    target->common.type));
30062306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	target->integer.value = 0;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Source Object can be either an op_region or a Buffer/Field */
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	switch (obj_desc->common.type) {
30862306a36Sopenharmony_ci	case ACPI_TYPE_REGION:
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
31162306a36Sopenharmony_ci				  "Load table from Region %p\n", obj_desc));
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* Region must be system_memory (from ACPI spec) */
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (obj_desc->region.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
31662306a36Sopenharmony_ci			return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		/*
32062306a36Sopenharmony_ci		 * If the Region Address and Length have not been previously
32162306a36Sopenharmony_ci		 * evaluated, evaluate them now and save the results.
32262306a36Sopenharmony_ci		 */
32362306a36Sopenharmony_ci		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
32462306a36Sopenharmony_ci			status = acpi_ds_get_region_arguments(obj_desc);
32562306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
32662306a36Sopenharmony_ci				return_ACPI_STATUS(status);
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		/* Get the table header first so we can get the table length */
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		table_header = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
33362306a36Sopenharmony_ci		if (!table_header) {
33462306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_MEMORY);
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		status =
33862306a36Sopenharmony_ci		    acpi_ex_region_read(obj_desc,
33962306a36Sopenharmony_ci					sizeof(struct acpi_table_header),
34062306a36Sopenharmony_ci					ACPI_CAST_PTR(u8, table_header));
34162306a36Sopenharmony_ci		length = table_header->length;
34262306a36Sopenharmony_ci		ACPI_FREE(table_header);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
34562306a36Sopenharmony_ci			return_ACPI_STATUS(status);
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		/* Must have at least an ACPI table header */
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		if (length < sizeof(struct acpi_table_header)) {
35162306a36Sopenharmony_ci			return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/*
35562306a36Sopenharmony_ci		 * The original implementation simply mapped the table, with no copy.
35662306a36Sopenharmony_ci		 * However, the memory region is not guaranteed to remain stable and
35762306a36Sopenharmony_ci		 * we must copy the table to a local buffer. For example, the memory
35862306a36Sopenharmony_ci		 * region is corrupted after suspend on some machines. Dynamically
35962306a36Sopenharmony_ci		 * loaded tables are usually small, so this overhead is minimal.
36062306a36Sopenharmony_ci		 *
36162306a36Sopenharmony_ci		 * The latest implementation (5/2009) does not use a mapping at all.
36262306a36Sopenharmony_ci		 * We use the low-level operation region interface to read the table
36362306a36Sopenharmony_ci		 * instead of the obvious optimization of using a direct mapping.
36462306a36Sopenharmony_ci		 * This maintains a consistent use of operation regions across the
36562306a36Sopenharmony_ci		 * entire subsystem. This is important if additional processing must
36662306a36Sopenharmony_ci		 * be performed in the (possibly user-installed) operation region
36762306a36Sopenharmony_ci		 * handler. For example, acpi_exec and ASLTS depend on this.
36862306a36Sopenharmony_ci		 */
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* Allocate a buffer for the table */
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		table = ACPI_ALLOCATE(length);
37362306a36Sopenharmony_ci		if (!table) {
37462306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_MEMORY);
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		/* Read the entire table */
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		status = acpi_ex_region_read(obj_desc, length,
38062306a36Sopenharmony_ci					     ACPI_CAST_PTR(u8, table));
38162306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
38262306a36Sopenharmony_ci			ACPI_FREE(table);
38362306a36Sopenharmony_ci			return_ACPI_STATUS(status);
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	case ACPI_TYPE_BUFFER:	/* Buffer or resolved region_field */
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
39062306a36Sopenharmony_ci				  "Load table from Buffer or Field %p\n",
39162306a36Sopenharmony_ci				  obj_desc));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* Must have at least an ACPI table header */
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		if (obj_desc->buffer.length < sizeof(struct acpi_table_header)) {
39662306a36Sopenharmony_ci			return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/* Get the actual table length from the table header */
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		table_header =
40262306a36Sopenharmony_ci		    ACPI_CAST_PTR(struct acpi_table_header,
40362306a36Sopenharmony_ci				  obj_desc->buffer.pointer);
40462306a36Sopenharmony_ci		length = table_header->length;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		/* Table cannot extend beyond the buffer */
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		if (length > obj_desc->buffer.length) {
40962306a36Sopenharmony_ci			return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci		if (length < sizeof(struct acpi_table_header)) {
41262306a36Sopenharmony_ci			return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		/*
41662306a36Sopenharmony_ci		 * Copy the table from the buffer because the buffer could be
41762306a36Sopenharmony_ci		 * modified or even deleted in the future
41862306a36Sopenharmony_ci		 */
41962306a36Sopenharmony_ci		table = ACPI_ALLOCATE(length);
42062306a36Sopenharmony_ci		if (!table) {
42162306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_MEMORY);
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		memcpy(table, table_header, length);
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	default:
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Install the new table into the local data structures */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ACPI_INFO(("Dynamic OEM Table Load:"));
43562306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
43662306a36Sopenharmony_ci	status = acpi_tb_install_and_load_table(ACPI_PTR_TO_PHYSADDR(table),
43762306a36Sopenharmony_ci						ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
43862306a36Sopenharmony_ci						table, TRUE, &table_index);
43962306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
44062306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		/* Delete allocated table buffer */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		ACPI_FREE(table);
44562306a36Sopenharmony_ci		return_ACPI_STATUS(status);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * Add the table to the namespace.
45062306a36Sopenharmony_ci	 *
45162306a36Sopenharmony_ci	 * Note: Load the table objects relative to the root of the namespace.
45262306a36Sopenharmony_ci	 * This appears to go against the ACPI specification, but we do it for
45362306a36Sopenharmony_ci	 * compatibility with other ACPI implementations.
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	status = acpi_ex_add_table(table_index, &ddb_handle);
45662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
45762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Complete the initialization/resolution of new objects */
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
46362306a36Sopenharmony_ci	acpi_ns_initialize_objects();
46462306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Remove the reference to ddb_handle created by acpi_ex_add_table above */
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	acpi_ut_remove_reference(ddb_handle);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Return -1 (non-zero) indicates success */
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	target->integer.value = 0xFFFFFFFFFFFFFFFF;
47362306a36Sopenharmony_ci	return_ACPI_STATUS(status);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/*******************************************************************************
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * FUNCTION:    acpi_ex_unload_table
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * PARAMETERS:  ddb_handle          - Handle to a previously loaded table
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * RETURN:      Status
48362306a36Sopenharmony_ci *
48462306a36Sopenharmony_ci * DESCRIPTION: Unload an ACPI table
48562306a36Sopenharmony_ci *
48662306a36Sopenharmony_ci ******************************************************************************/
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciacpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	acpi_status status = AE_OK;
49162306a36Sopenharmony_ci	union acpi_operand_object *table_desc = ddb_handle;
49262306a36Sopenharmony_ci	u32 table_index;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_unload_table);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * Temporarily emit a warning so that the ASL for the machine can be
49862306a36Sopenharmony_ci	 * hopefully obtained. This is to say that the Unload() operator is
49962306a36Sopenharmony_ci	 * extremely rare if not completely unused.
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	ACPI_WARNING((AE_INFO, "Received request to unload an ACPI table"));
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * May 2018: Unload is no longer supported for the following reasons:
50562306a36Sopenharmony_ci	 * 1) A correct implementation on some hosts may not be possible.
50662306a36Sopenharmony_ci	 * 2) Other ACPI implementations do not correctly/fully support it.
50762306a36Sopenharmony_ci	 * 3) It requires host device driver support which does not exist.
50862306a36Sopenharmony_ci	 *    (To properly support namespace unload out from underneath.)
50962306a36Sopenharmony_ci	 * 4) This AML operator has never been seen in the field.
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci	ACPI_EXCEPTION((AE_INFO, AE_NOT_IMPLEMENTED,
51262306a36Sopenharmony_ci			"AML Unload operator is not supported"));
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/*
51562306a36Sopenharmony_ci	 * Validate the handle
51662306a36Sopenharmony_ci	 * Although the handle is partially validated in acpi_ex_reconfiguration()
51762306a36Sopenharmony_ci	 * when it calls acpi_ex_resolve_operands(), the handle is more completely
51862306a36Sopenharmony_ci	 * validated here.
51962306a36Sopenharmony_ci	 *
52062306a36Sopenharmony_ci	 * Handle must be a valid operand object of type reference. Also, the
52162306a36Sopenharmony_ci	 * ddb_handle must still be marked valid (table has not been previously
52262306a36Sopenharmony_ci	 * unloaded)
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	if ((!ddb_handle) ||
52562306a36Sopenharmony_ci	    (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) ||
52662306a36Sopenharmony_ci	    (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) ||
52762306a36Sopenharmony_ci	    (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) {
52862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Get the table index from the ddb_handle */
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	table_index = table_desc->reference.value;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * Release the interpreter lock so that the table lock won't have
53762306a36Sopenharmony_ci	 * strict order requirement against it.
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	acpi_ex_exit_interpreter();
54062306a36Sopenharmony_ci	status = acpi_tb_unload_table(table_index);
54162306a36Sopenharmony_ci	acpi_ex_enter_interpreter();
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/*
54462306a36Sopenharmony_ci	 * Invalidate the handle. We do this because the handle may be stored
54562306a36Sopenharmony_ci	 * in a named object and may not be actually deleted until much later.
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
54862306a36Sopenharmony_ci		ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
55162306a36Sopenharmony_ci}
552