GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / acpi / acpica / utaddress.c
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 - 2018, 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 }