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 - 2023, 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(node: 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 | |