162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: utaddress - op_region address range check
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 "acnamesp.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define _COMPONENT          ACPI_UTILITIES
1562306a36Sopenharmony_ciACPI_MODULE_NAME("utaddress")
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*******************************************************************************
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * FUNCTION:    acpi_ut_add_address_range
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * PARAMETERS:  space_id            - Address space ID
2262306a36Sopenharmony_ci *              address             - op_region start address
2362306a36Sopenharmony_ci *              length              - op_region length
2462306a36Sopenharmony_ci *              region_node         - op_region namespace node
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * RETURN:      Status
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * DESCRIPTION: Add the Operation Region address range to the global list.
2962306a36Sopenharmony_ci *              The only supported Space IDs are Memory and I/O. Called when
3062306a36Sopenharmony_ci *              the op_region address/length operands are fully evaluated.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * MUTEX:       Locks the namespace
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * NOTE: Because this interface is only called when an op_region argument
3562306a36Sopenharmony_ci * list is evaluated, there cannot be any duplicate region_nodes.
3662306a36Sopenharmony_ci * Duplicate Address/Length values are allowed, however, so that multiple
3762306a36Sopenharmony_ci * address conflicts can be detected.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci ******************************************************************************/
4062306a36Sopenharmony_ciacpi_status
4162306a36Sopenharmony_ciacpi_ut_add_address_range(acpi_adr_space_type space_id,
4262306a36Sopenharmony_ci			  acpi_physical_address address,
4362306a36Sopenharmony_ci			  u32 length, struct acpi_namespace_node *region_node)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct acpi_address_range *range_info;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ut_add_address_range);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
5062306a36Sopenharmony_ci	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
5162306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Allocate/init a new info block, add it to the appropriate list */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	range_info = ACPI_ALLOCATE(sizeof(struct acpi_address_range));
5762306a36Sopenharmony_ci	if (!range_info) {
5862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	range_info->start_address = address;
6262306a36Sopenharmony_ci	range_info->end_address = (address + length - 1);
6362306a36Sopenharmony_ci	range_info->region_node = region_node;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	range_info->next = acpi_gbl_address_range_list[space_id];
6662306a36Sopenharmony_ci	acpi_gbl_address_range_list[space_id] = range_info;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
6962306a36Sopenharmony_ci			  "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
7062306a36Sopenharmony_ci			  acpi_ut_get_node_name(range_info->region_node),
7162306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(address),
7262306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(range_info->end_address)));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*******************************************************************************
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * FUNCTION:    acpi_ut_remove_address_range
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * PARAMETERS:  space_id            - Address space ID
8262306a36Sopenharmony_ci *              region_node         - op_region namespace node
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * RETURN:      None
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * DESCRIPTION: Remove the Operation Region from the global list. The only
8762306a36Sopenharmony_ci *              supported Space IDs are Memory and I/O. Called when an
8862306a36Sopenharmony_ci *              op_region is deleted.
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * MUTEX:       Assumes the namespace is locked
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci ******************************************************************************/
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_civoid
9562306a36Sopenharmony_ciacpi_ut_remove_address_range(acpi_adr_space_type space_id,
9662306a36Sopenharmony_ci			     struct acpi_namespace_node *region_node)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct acpi_address_range *range_info;
9962306a36Sopenharmony_ci	struct acpi_address_range *prev;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ut_remove_address_range);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
10462306a36Sopenharmony_ci	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
10562306a36Sopenharmony_ci		return_VOID;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Get the appropriate list head and check the list */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	range_info = prev = acpi_gbl_address_range_list[space_id];
11162306a36Sopenharmony_ci	while (range_info) {
11262306a36Sopenharmony_ci		if (range_info->region_node == region_node) {
11362306a36Sopenharmony_ci			if (range_info == prev) {	/* Found at list head */
11462306a36Sopenharmony_ci				acpi_gbl_address_range_list[space_id] =
11562306a36Sopenharmony_ci				    range_info->next;
11662306a36Sopenharmony_ci			} else {
11762306a36Sopenharmony_ci				prev->next = range_info->next;
11862306a36Sopenharmony_ci			}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
12162306a36Sopenharmony_ci					  "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
12262306a36Sopenharmony_ci					  acpi_ut_get_node_name(range_info->
12362306a36Sopenharmony_ci								region_node),
12462306a36Sopenharmony_ci					  ACPI_FORMAT_UINT64(range_info->
12562306a36Sopenharmony_ci							     start_address),
12662306a36Sopenharmony_ci					  ACPI_FORMAT_UINT64(range_info->
12762306a36Sopenharmony_ci							     end_address)));
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci			ACPI_FREE(range_info);
13062306a36Sopenharmony_ci			return_VOID;
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		prev = range_info;
13462306a36Sopenharmony_ci		range_info = range_info->next;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return_VOID;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*******************************************************************************
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * FUNCTION:    acpi_ut_check_address_range
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci * PARAMETERS:  space_id            - Address space ID
14562306a36Sopenharmony_ci *              address             - Start address
14662306a36Sopenharmony_ci *              length              - Length of address range
14762306a36Sopenharmony_ci *              warn                - TRUE if warning on overlap desired
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * RETURN:      Count of the number of conflicts detected. Zero is always
15062306a36Sopenharmony_ci *              returned for Space IDs other than Memory or I/O.
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * DESCRIPTION: Check if the input address range overlaps any of the
15362306a36Sopenharmony_ci *              ASL operation region address ranges. The only supported
15462306a36Sopenharmony_ci *              Space IDs are Memory and I/O.
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * MUTEX:       Assumes the namespace is locked.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci ******************************************************************************/
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ciu32
16162306a36Sopenharmony_ciacpi_ut_check_address_range(acpi_adr_space_type space_id,
16262306a36Sopenharmony_ci			    acpi_physical_address address, u32 length, u8 warn)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct acpi_address_range *range_info;
16562306a36Sopenharmony_ci	acpi_physical_address end_address;
16662306a36Sopenharmony_ci	char *pathname;
16762306a36Sopenharmony_ci	u32 overlap_count = 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ut_check_address_range);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
17262306a36Sopenharmony_ci	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
17362306a36Sopenharmony_ci		return_UINT32(0);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	range_info = acpi_gbl_address_range_list[space_id];
17762306a36Sopenharmony_ci	end_address = address + length - 1;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Check entire list for all possible conflicts */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	while (range_info) {
18262306a36Sopenharmony_ci		/*
18362306a36Sopenharmony_ci		 * Check if the requested address/length overlaps this
18462306a36Sopenharmony_ci		 * address range. There are four cases to consider:
18562306a36Sopenharmony_ci		 *
18662306a36Sopenharmony_ci		 * 1) Input address/length is contained completely in the
18762306a36Sopenharmony_ci		 *    address range
18862306a36Sopenharmony_ci		 * 2) Input address/length overlaps range at the range start
18962306a36Sopenharmony_ci		 * 3) Input address/length overlaps range at the range end
19062306a36Sopenharmony_ci		 * 4) Input address/length completely encompasses the range
19162306a36Sopenharmony_ci		 */
19262306a36Sopenharmony_ci		if ((address <= range_info->end_address) &&
19362306a36Sopenharmony_ci		    (end_address >= range_info->start_address)) {
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci			/* Found an address range overlap */
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci			overlap_count++;
19862306a36Sopenharmony_ci			if (warn) {	/* Optional warning message */
19962306a36Sopenharmony_ci				pathname =
20062306a36Sopenharmony_ci				    acpi_ns_get_normalized_pathname(range_info->
20162306a36Sopenharmony_ci								    region_node,
20262306a36Sopenharmony_ci								    TRUE);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci				ACPI_WARNING((AE_INFO,
20562306a36Sopenharmony_ci					      "%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)",
20662306a36Sopenharmony_ci					      acpi_ut_get_region_name(space_id),
20762306a36Sopenharmony_ci					      ACPI_FORMAT_UINT64(address),
20862306a36Sopenharmony_ci					      ACPI_FORMAT_UINT64(end_address),
20962306a36Sopenharmony_ci					      ACPI_FORMAT_UINT64(range_info->
21062306a36Sopenharmony_ci								 start_address),
21162306a36Sopenharmony_ci					      ACPI_FORMAT_UINT64(range_info->
21262306a36Sopenharmony_ci								 end_address),
21362306a36Sopenharmony_ci					      pathname));
21462306a36Sopenharmony_ci				ACPI_FREE(pathname);
21562306a36Sopenharmony_ci			}
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		range_info = range_info->next;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return_UINT32(overlap_count);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/*******************************************************************************
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * FUNCTION:    acpi_ut_delete_address_lists
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci * PARAMETERS:  None
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * RETURN:      None
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * DESCRIPTION: Delete all global address range lists (called during
23362306a36Sopenharmony_ci *              subsystem shutdown).
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci ******************************************************************************/
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_civoid acpi_ut_delete_address_lists(void)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct acpi_address_range *next;
24062306a36Sopenharmony_ci	struct acpi_address_range *range_info;
24162306a36Sopenharmony_ci	int i;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* Delete all elements in all address range lists */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) {
24662306a36Sopenharmony_ci		next = acpi_gbl_address_range_list[i];
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		while (next) {
24962306a36Sopenharmony_ci			range_info = next;
25062306a36Sopenharmony_ci			next = range_info->next;
25162306a36Sopenharmony_ci			ACPI_FREE(range_info);
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		acpi_gbl_address_range_list[i] = NULL;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
257