1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /******************************************************************************* |
3 | * |
4 | * Module Name: nsnames - Name manipulation and search |
5 | * |
6 | ******************************************************************************/ |
7 | |
8 | #include <acpi/acpi.h> |
9 | #include "accommon.h" |
10 | #include "amlcode.h" |
11 | #include "acnamesp.h" |
12 | |
13 | #define _COMPONENT ACPI_NAMESPACE |
14 | ACPI_MODULE_NAME("nsnames" ) |
15 | |
16 | /******************************************************************************* |
17 | * |
18 | * FUNCTION: acpi_ns_get_external_pathname |
19 | * |
20 | * PARAMETERS: node - Namespace node whose pathname is needed |
21 | * |
22 | * RETURN: Pointer to storage containing the fully qualified name of |
23 | * the node, In external format (name segments separated by path |
24 | * separators.) |
25 | * |
26 | * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually |
27 | * for error and debug statements. |
28 | * |
29 | ******************************************************************************/ |
30 | char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) |
31 | { |
32 | char *name_buffer; |
33 | |
34 | ACPI_FUNCTION_TRACE_PTR(ns_get_external_pathname, node); |
35 | |
36 | name_buffer = acpi_ns_get_normalized_pathname(node, FALSE); |
37 | return_PTR(name_buffer); |
38 | } |
39 | |
40 | /******************************************************************************* |
41 | * |
42 | * FUNCTION: acpi_ns_get_pathname_length |
43 | * |
44 | * PARAMETERS: node - Namespace node |
45 | * |
46 | * RETURN: Length of path, including prefix |
47 | * |
48 | * DESCRIPTION: Get the length of the pathname string for this node |
49 | * |
50 | ******************************************************************************/ |
51 | |
52 | acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node) |
53 | { |
54 | acpi_size size; |
55 | |
56 | /* Validate the Node */ |
57 | |
58 | if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { |
59 | ACPI_ERROR((AE_INFO, |
60 | "Invalid/cached reference target node: %p, descriptor type %d" , |
61 | node, ACPI_GET_DESCRIPTOR_TYPE(node))); |
62 | return (0); |
63 | } |
64 | |
65 | size = acpi_ns_build_normalized_path(node, NULL, path_size: 0, FALSE); |
66 | return (size); |
67 | } |
68 | |
69 | /******************************************************************************* |
70 | * |
71 | * FUNCTION: acpi_ns_handle_to_name |
72 | * |
73 | * PARAMETERS: target_handle - Handle of named object whose name is |
74 | * to be found |
75 | * buffer - Where the name is returned |
76 | * |
77 | * RETURN: Status, Buffer is filled with name if status is AE_OK |
78 | * |
79 | * DESCRIPTION: Build and return a full namespace name |
80 | * |
81 | ******************************************************************************/ |
82 | |
83 | acpi_status |
84 | acpi_ns_handle_to_name(acpi_handle target_handle, struct acpi_buffer *buffer) |
85 | { |
86 | acpi_status status; |
87 | struct acpi_namespace_node *node; |
88 | const char *node_name; |
89 | |
90 | ACPI_FUNCTION_TRACE_PTR(ns_handle_to_name, target_handle); |
91 | |
92 | node = acpi_ns_validate_handle(handle: target_handle); |
93 | if (!node) { |
94 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
95 | } |
96 | |
97 | /* Validate/Allocate/Clear caller buffer */ |
98 | |
99 | status = acpi_ut_initialize_buffer(buffer, ACPI_PATH_SEGMENT_LENGTH); |
100 | if (ACPI_FAILURE(status)) { |
101 | return_ACPI_STATUS(status); |
102 | } |
103 | |
104 | /* Just copy the ACPI name from the Node and zero terminate it */ |
105 | |
106 | node_name = acpi_ut_get_node_name(object: node); |
107 | ACPI_COPY_NAMESEG(buffer->pointer, node_name); |
108 | ((char *)buffer->pointer)[ACPI_NAMESEG_SIZE] = 0; |
109 | |
110 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%4.4s\n" , (char *)buffer->pointer)); |
111 | return_ACPI_STATUS(AE_OK); |
112 | } |
113 | |
114 | /******************************************************************************* |
115 | * |
116 | * FUNCTION: acpi_ns_handle_to_pathname |
117 | * |
118 | * PARAMETERS: target_handle - Handle of named object whose name is |
119 | * to be found |
120 | * buffer - Where the pathname is returned |
121 | * no_trailing - Remove trailing '_' for each name |
122 | * segment |
123 | * |
124 | * RETURN: Status, Buffer is filled with pathname if status is AE_OK |
125 | * |
126 | * DESCRIPTION: Build and return a full namespace pathname |
127 | * |
128 | ******************************************************************************/ |
129 | |
130 | acpi_status |
131 | acpi_ns_handle_to_pathname(acpi_handle target_handle, |
132 | struct acpi_buffer *buffer, u8 no_trailing) |
133 | { |
134 | acpi_status status; |
135 | struct acpi_namespace_node *node; |
136 | acpi_size required_size; |
137 | |
138 | ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, target_handle); |
139 | |
140 | node = acpi_ns_validate_handle(handle: target_handle); |
141 | if (!node) { |
142 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
143 | } |
144 | |
145 | /* Determine size required for the caller buffer */ |
146 | |
147 | required_size = |
148 | acpi_ns_build_normalized_path(node, NULL, path_size: 0, no_trailing); |
149 | if (!required_size) { |
150 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
151 | } |
152 | |
153 | /* Validate/Allocate/Clear caller buffer */ |
154 | |
155 | status = acpi_ut_initialize_buffer(buffer, required_length: required_size); |
156 | if (ACPI_FAILURE(status)) { |
157 | return_ACPI_STATUS(status); |
158 | } |
159 | |
160 | /* Build the path in the caller buffer */ |
161 | |
162 | (void)acpi_ns_build_normalized_path(node, full_path: buffer->pointer, |
163 | path_size: (u32)required_size, no_trailing); |
164 | |
165 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%X]\n" , |
166 | (char *)buffer->pointer, (u32) required_size)); |
167 | return_ACPI_STATUS(AE_OK); |
168 | } |
169 | |
170 | /******************************************************************************* |
171 | * |
172 | * FUNCTION: acpi_ns_build_normalized_path |
173 | * |
174 | * PARAMETERS: node - Namespace node |
175 | * full_path - Where the path name is returned |
176 | * path_size - Size of returned path name buffer |
177 | * no_trailing - Remove trailing '_' from each name segment |
178 | * |
179 | * RETURN: Return 1 if the AML path is empty, otherwise returning (length |
180 | * of pathname + 1) which means the 'FullPath' contains a trailing |
181 | * null. |
182 | * |
183 | * DESCRIPTION: Build and return a full namespace pathname. |
184 | * Note that if the size of 'FullPath' isn't large enough to |
185 | * contain the namespace node's path name, the actual required |
186 | * buffer length is returned, and it should be greater than |
187 | * 'PathSize'. So callers are able to check the returning value |
188 | * to determine the buffer size of 'FullPath'. |
189 | * |
190 | ******************************************************************************/ |
191 | |
192 | u32 |
193 | acpi_ns_build_normalized_path(struct acpi_namespace_node *node, |
194 | char *full_path, u32 path_size, u8 no_trailing) |
195 | { |
196 | u32 length = 0, i; |
197 | char name[ACPI_NAMESEG_SIZE]; |
198 | u8 do_no_trailing; |
199 | char c, *left, *right; |
200 | struct acpi_namespace_node *next_node; |
201 | |
202 | ACPI_FUNCTION_TRACE_PTR(ns_build_normalized_path, node); |
203 | |
204 | #define ACPI_PATH_PUT8(path, size, byte, length) \ |
205 | do { \ |
206 | if ((length) < (size)) \ |
207 | { \ |
208 | (path)[(length)] = (byte); \ |
209 | } \ |
210 | (length)++; \ |
211 | } while (0) |
212 | |
213 | /* |
214 | * Make sure the path_size is correct, so that we don't need to |
215 | * validate both full_path and path_size. |
216 | */ |
217 | if (!full_path) { |
218 | path_size = 0; |
219 | } |
220 | |
221 | if (!node) { |
222 | goto build_trailing_null; |
223 | } |
224 | |
225 | next_node = node; |
226 | while (next_node && next_node != acpi_gbl_root_node) { |
227 | if (next_node != node) { |
228 | ACPI_PATH_PUT8(full_path, path_size, |
229 | AML_DUAL_NAME_PREFIX, length); |
230 | } |
231 | |
232 | ACPI_MOVE_32_TO_32(name, &next_node->name); |
233 | do_no_trailing = no_trailing; |
234 | for (i = 0; i < 4; i++) { |
235 | c = name[4 - i - 1]; |
236 | if (do_no_trailing && c != '_') { |
237 | do_no_trailing = FALSE; |
238 | } |
239 | if (!do_no_trailing) { |
240 | ACPI_PATH_PUT8(full_path, path_size, c, length); |
241 | } |
242 | } |
243 | |
244 | next_node = next_node->parent; |
245 | } |
246 | |
247 | ACPI_PATH_PUT8(full_path, path_size, AML_ROOT_PREFIX, length); |
248 | |
249 | /* Reverse the path string */ |
250 | |
251 | if (length <= path_size) { |
252 | left = full_path; |
253 | right = full_path + length - 1; |
254 | |
255 | while (left < right) { |
256 | c = *left; |
257 | *left++ = *right; |
258 | *right-- = c; |
259 | } |
260 | } |
261 | |
262 | /* Append the trailing null */ |
263 | |
264 | build_trailing_null: |
265 | ACPI_PATH_PUT8(full_path, path_size, '\0', length); |
266 | |
267 | #undef ACPI_PATH_PUT8 |
268 | |
269 | return_UINT32(length); |
270 | } |
271 | |
272 | /******************************************************************************* |
273 | * |
274 | * FUNCTION: acpi_ns_get_normalized_pathname |
275 | * |
276 | * PARAMETERS: node - Namespace node whose pathname is needed |
277 | * no_trailing - Remove trailing '_' from each name segment |
278 | * |
279 | * RETURN: Pointer to storage containing the fully qualified name of |
280 | * the node, In external format (name segments separated by path |
281 | * separators.) |
282 | * |
283 | * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually |
284 | * for error and debug statements. All trailing '_' will be |
285 | * removed from the full pathname if 'NoTrailing' is specified.. |
286 | * |
287 | ******************************************************************************/ |
288 | |
289 | char *acpi_ns_get_normalized_pathname(struct acpi_namespace_node *node, |
290 | u8 no_trailing) |
291 | { |
292 | char *name_buffer; |
293 | acpi_size size; |
294 | |
295 | ACPI_FUNCTION_TRACE_PTR(ns_get_normalized_pathname, node); |
296 | |
297 | /* Calculate required buffer size based on depth below root */ |
298 | |
299 | size = acpi_ns_build_normalized_path(node, NULL, path_size: 0, no_trailing); |
300 | if (!size) { |
301 | return_PTR(NULL); |
302 | } |
303 | |
304 | /* Allocate a buffer to be returned to caller */ |
305 | |
306 | name_buffer = ACPI_ALLOCATE_ZEROED(size); |
307 | if (!name_buffer) { |
308 | ACPI_ERROR((AE_INFO, "Could not allocate %u bytes" , (u32)size)); |
309 | return_PTR(NULL); |
310 | } |
311 | |
312 | /* Build the path in the allocated buffer */ |
313 | |
314 | (void)acpi_ns_build_normalized_path(node, full_path: name_buffer, path_size: (u32)size, |
315 | no_trailing); |
316 | |
317 | ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, "%s: Path \"%s\"\n" , |
318 | ACPI_GET_FUNCTION_NAME, name_buffer)); |
319 | |
320 | return_PTR(name_buffer); |
321 | } |
322 | |
323 | /******************************************************************************* |
324 | * |
325 | * FUNCTION: acpi_ns_build_prefixed_pathname |
326 | * |
327 | * PARAMETERS: prefix_scope - Scope/Path that prefixes the internal path |
328 | * internal_path - Name or path of the namespace node |
329 | * |
330 | * RETURN: None |
331 | * |
332 | * DESCRIPTION: Construct a fully qualified pathname from a concatenation of: |
333 | * 1) Path associated with the prefix_scope namespace node |
334 | * 2) External path representation of the Internal path |
335 | * |
336 | ******************************************************************************/ |
337 | |
338 | char *acpi_ns_build_prefixed_pathname(union acpi_generic_state *prefix_scope, |
339 | const char *internal_path) |
340 | { |
341 | acpi_status status; |
342 | char *full_path = NULL; |
343 | char *external_path = NULL; |
344 | char *prefix_path = NULL; |
345 | acpi_size prefix_path_length = 0; |
346 | |
347 | /* If there is a prefix, get the pathname to it */ |
348 | |
349 | if (prefix_scope && prefix_scope->scope.node) { |
350 | prefix_path = |
351 | acpi_ns_get_normalized_pathname(node: prefix_scope->scope.node, |
352 | TRUE); |
353 | if (prefix_path) { |
354 | prefix_path_length = strlen(prefix_path); |
355 | } |
356 | } |
357 | |
358 | status = acpi_ns_externalize_name(ACPI_UINT32_MAX, internal_name: internal_path, |
359 | NULL, converted_name: &external_path); |
360 | if (ACPI_FAILURE(status)) { |
361 | goto cleanup; |
362 | } |
363 | |
364 | /* Merge the prefix path and the path. 2 is for one dot and trailing null */ |
365 | |
366 | full_path = |
367 | ACPI_ALLOCATE_ZEROED(prefix_path_length + strlen(external_path) + |
368 | 2); |
369 | if (!full_path) { |
370 | goto cleanup; |
371 | } |
372 | |
373 | /* Don't merge if the External path is already fully qualified */ |
374 | |
375 | if (prefix_path && (*external_path != '\\') && (*external_path != '^')) { |
376 | strcat(p: full_path, q: prefix_path); |
377 | if (prefix_path[1]) { |
378 | strcat(p: full_path, q: "." ); |
379 | } |
380 | } |
381 | |
382 | acpi_ns_normalize_pathname(original_path: external_path); |
383 | strcat(p: full_path, q: external_path); |
384 | |
385 | cleanup: |
386 | if (prefix_path) { |
387 | ACPI_FREE(prefix_path); |
388 | } |
389 | if (external_path) { |
390 | ACPI_FREE(external_path); |
391 | } |
392 | |
393 | return (full_path); |
394 | } |
395 | |
396 | /******************************************************************************* |
397 | * |
398 | * FUNCTION: acpi_ns_normalize_pathname |
399 | * |
400 | * PARAMETERS: original_path - Path to be normalized, in External format |
401 | * |
402 | * RETURN: The original path is processed in-place |
403 | * |
404 | * DESCRIPTION: Remove trailing underscores from each element of a path. |
405 | * |
406 | * For example: \A___.B___.C___ becomes \A.B.C |
407 | * |
408 | ******************************************************************************/ |
409 | |
410 | void acpi_ns_normalize_pathname(char *original_path) |
411 | { |
412 | char *input_path = original_path; |
413 | char *new_path_buffer; |
414 | char *new_path; |
415 | u32 i; |
416 | |
417 | /* Allocate a temp buffer in which to construct the new path */ |
418 | |
419 | new_path_buffer = ACPI_ALLOCATE_ZEROED(strlen(input_path) + 1); |
420 | new_path = new_path_buffer; |
421 | if (!new_path_buffer) { |
422 | return; |
423 | } |
424 | |
425 | /* Special characters may appear at the beginning of the path */ |
426 | |
427 | if (*input_path == '\\') { |
428 | *new_path = *input_path; |
429 | new_path++; |
430 | input_path++; |
431 | } |
432 | |
433 | while (*input_path == '^') { |
434 | *new_path = *input_path; |
435 | new_path++; |
436 | input_path++; |
437 | } |
438 | |
439 | /* Remainder of the path */ |
440 | |
441 | while (*input_path) { |
442 | |
443 | /* Do one nameseg at a time */ |
444 | |
445 | for (i = 0; (i < ACPI_NAMESEG_SIZE) && *input_path; i++) { |
446 | if ((i == 0) || (*input_path != '_')) { /* First char is allowed to be underscore */ |
447 | *new_path = *input_path; |
448 | new_path++; |
449 | } |
450 | |
451 | input_path++; |
452 | } |
453 | |
454 | /* Dot means that there are more namesegs to come */ |
455 | |
456 | if (*input_path == '.') { |
457 | *new_path = *input_path; |
458 | new_path++; |
459 | input_path++; |
460 | } |
461 | } |
462 | |
463 | *new_path = 0; |
464 | strcpy(p: original_path, q: new_path_buffer); |
465 | ACPI_FREE(new_path_buffer); |
466 | } |
467 | |