xref: /openbmc/linux/drivers/acpi/acpica/utaddress.c (revision 6f4eaea2)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /******************************************************************************
3  *
4  * Module Name: utaddress - op_region address range check
5  *
6  * Copyright (C) 2000 - 2021, Intel Corp.
7  *
8  *****************************************************************************/
9 
10 #include <acpi/acpi.h>
11 #include "accommon.h"
12 #include "acnamesp.h"
13 
14 #define _COMPONENT          ACPI_UTILITIES
15 ACPI_MODULE_NAME("utaddress")
16 
17 /*******************************************************************************
18  *
19  * FUNCTION:    acpi_ut_add_address_range
20  *
21  * PARAMETERS:  space_id            - Address space ID
22  *              address             - op_region start address
23  *              length              - op_region length
24  *              region_node         - op_region namespace node
25  *
26  * RETURN:      Status
27  *
28  * DESCRIPTION: Add the Operation Region address range to the global list.
29  *              The only supported Space IDs are Memory and I/O. Called when
30  *              the op_region address/length operands are fully evaluated.
31  *
32  * MUTEX:       Locks the namespace
33  *
34  * NOTE: Because this interface is only called when an op_region argument
35  * list is evaluated, there cannot be any duplicate region_nodes.
36  * Duplicate Address/Length values are allowed, however, so that multiple
37  * address conflicts can be detected.
38  *
39  ******************************************************************************/
40 acpi_status
41 acpi_ut_add_address_range(acpi_adr_space_type space_id,
42 			  acpi_physical_address address,
43 			  u32 length, struct acpi_namespace_node *region_node)
44 {
45 	struct acpi_address_range *range_info;
46 
47 	ACPI_FUNCTION_TRACE(ut_add_address_range);
48 
49 	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
50 	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
51 		return_ACPI_STATUS(AE_OK);
52 	}
53 
54 	/* Allocate/init a new info block, add it to the appropriate list */
55 
56 	range_info = ACPI_ALLOCATE(sizeof(struct acpi_address_range));
57 	if (!range_info) {
58 		return_ACPI_STATUS(AE_NO_MEMORY);
59 	}
60 
61 	range_info->start_address = address;
62 	range_info->end_address = (address + length - 1);
63 	range_info->region_node = region_node;
64 
65 	range_info->next = acpi_gbl_address_range_list[space_id];
66 	acpi_gbl_address_range_list[space_id] = range_info;
67 
68 	ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
69 			  "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
70 			  acpi_ut_get_node_name(range_info->region_node),
71 			  ACPI_FORMAT_UINT64(address),
72 			  ACPI_FORMAT_UINT64(range_info->end_address)));
73 
74 	return_ACPI_STATUS(AE_OK);
75 }
76 
77 /*******************************************************************************
78  *
79  * FUNCTION:    acpi_ut_remove_address_range
80  *
81  * PARAMETERS:  space_id            - Address space ID
82  *              region_node         - op_region namespace node
83  *
84  * RETURN:      None
85  *
86  * DESCRIPTION: Remove the Operation Region from the global list. The only
87  *              supported Space IDs are Memory and I/O. Called when an
88  *              op_region is deleted.
89  *
90  * MUTEX:       Assumes the namespace is locked
91  *
92  ******************************************************************************/
93 
94 void
95 acpi_ut_remove_address_range(acpi_adr_space_type space_id,
96 			     struct acpi_namespace_node *region_node)
97 {
98 	struct acpi_address_range *range_info;
99 	struct acpi_address_range *prev;
100 
101 	ACPI_FUNCTION_TRACE(ut_remove_address_range);
102 
103 	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
104 	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
105 		return_VOID;
106 	}
107 
108 	/* Get the appropriate list head and check the list */
109 
110 	range_info = prev = acpi_gbl_address_range_list[space_id];
111 	while (range_info) {
112 		if (range_info->region_node == region_node) {
113 			if (range_info == prev) {	/* Found at list head */
114 				acpi_gbl_address_range_list[space_id] =
115 				    range_info->next;
116 			} else {
117 				prev->next = range_info->next;
118 			}
119 
120 			ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
121 					  "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n",
122 					  acpi_ut_get_node_name(range_info->
123 								region_node),
124 					  ACPI_FORMAT_UINT64(range_info->
125 							     start_address),
126 					  ACPI_FORMAT_UINT64(range_info->
127 							     end_address)));
128 
129 			ACPI_FREE(range_info);
130 			return_VOID;
131 		}
132 
133 		prev = range_info;
134 		range_info = range_info->next;
135 	}
136 
137 	return_VOID;
138 }
139 
140 /*******************************************************************************
141  *
142  * FUNCTION:    acpi_ut_check_address_range
143  *
144  * PARAMETERS:  space_id            - Address space ID
145  *              address             - Start address
146  *              length              - Length of address range
147  *              warn                - TRUE if warning on overlap desired
148  *
149  * RETURN:      Count of the number of conflicts detected. Zero is always
150  *              returned for Space IDs other than Memory or I/O.
151  *
152  * DESCRIPTION: Check if the input address range overlaps any of the
153  *              ASL operation region address ranges. The only supported
154  *              Space IDs are Memory and I/O.
155  *
156  * MUTEX:       Assumes the namespace is locked.
157  *
158  ******************************************************************************/
159 
160 u32
161 acpi_ut_check_address_range(acpi_adr_space_type space_id,
162 			    acpi_physical_address address, u32 length, u8 warn)
163 {
164 	struct acpi_address_range *range_info;
165 	acpi_physical_address end_address;
166 	char *pathname;
167 	u32 overlap_count = 0;
168 
169 	ACPI_FUNCTION_TRACE(ut_check_address_range);
170 
171 	if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
172 	    (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
173 		return_UINT32(0);
174 	}
175 
176 	range_info = acpi_gbl_address_range_list[space_id];
177 	end_address = address + length - 1;
178 
179 	/* Check entire list for all possible conflicts */
180 
181 	while (range_info) {
182 		/*
183 		 * Check if the requested address/length overlaps this
184 		 * address range. There are four cases to consider:
185 		 *
186 		 * 1) Input address/length is contained completely in the
187 		 *    address range
188 		 * 2) Input address/length overlaps range at the range start
189 		 * 3) Input address/length overlaps range at the range end
190 		 * 4) Input address/length completely encompasses the range
191 		 */
192 		if ((address <= range_info->end_address) &&
193 		    (end_address >= range_info->start_address)) {
194 
195 			/* Found an address range overlap */
196 
197 			overlap_count++;
198 			if (warn) {	/* Optional warning message */
199 				pathname =
200 				    acpi_ns_get_normalized_pathname(range_info->
201 								    region_node,
202 								    TRUE);
203 
204 				ACPI_WARNING((AE_INFO,
205 					      "%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)",
206 					      acpi_ut_get_region_name(space_id),
207 					      ACPI_FORMAT_UINT64(address),
208 					      ACPI_FORMAT_UINT64(end_address),
209 					      ACPI_FORMAT_UINT64(range_info->
210 								 start_address),
211 					      ACPI_FORMAT_UINT64(range_info->
212 								 end_address),
213 					      pathname));
214 				ACPI_FREE(pathname);
215 			}
216 		}
217 
218 		range_info = range_info->next;
219 	}
220 
221 	return_UINT32(overlap_count);
222 }
223 
224 /*******************************************************************************
225  *
226  * FUNCTION:    acpi_ut_delete_address_lists
227  *
228  * PARAMETERS:  None
229  *
230  * RETURN:      None
231  *
232  * DESCRIPTION: Delete all global address range lists (called during
233  *              subsystem shutdown).
234  *
235  ******************************************************************************/
236 
237 void acpi_ut_delete_address_lists(void)
238 {
239 	struct acpi_address_range *next;
240 	struct acpi_address_range *range_info;
241 	int i;
242 
243 	/* Delete all elements in all address range lists */
244 
245 	for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) {
246 		next = acpi_gbl_address_range_list[i];
247 
248 		while (next) {
249 			range_info = next;
250 			next = range_info->next;
251 			ACPI_FREE(range_info);
252 		}
253 
254 		acpi_gbl_address_range_list[i] = NULL;
255 	}
256 }
257