1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: nsxfname - Public interfaces to the ACPI subsystem |
5 | * ACPI Namespace oriented interfaces |
6 | * |
7 | * Copyright (C) 2000 - 2023, Intel Corp. |
8 | * |
9 | *****************************************************************************/ |
10 | |
11 | #define EXPORT_ACPI_INTERFACES |
12 | |
13 | #include <acpi/acpi.h> |
14 | #include "accommon.h" |
15 | #include "acnamesp.h" |
16 | #include "acparser.h" |
17 | #include "amlcode.h" |
18 | |
19 | #define _COMPONENT ACPI_NAMESPACE |
20 | ACPI_MODULE_NAME("nsxfname" ) |
21 | |
22 | /* Local prototypes */ |
23 | static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest, |
24 | struct acpi_pnp_device_id *source, |
25 | char *string_area); |
26 | |
27 | /****************************************************************************** |
28 | * |
29 | * FUNCTION: acpi_get_handle |
30 | * |
31 | * PARAMETERS: parent - Object to search under (search scope). |
32 | * pathname - Pointer to an asciiz string containing the |
33 | * name |
34 | * ret_handle - Where the return handle is returned |
35 | * |
36 | * RETURN: Status |
37 | * |
38 | * DESCRIPTION: This routine will search for a caller specified name in the |
39 | * name space. The caller can restrict the search region by |
40 | * specifying a non NULL parent. The parent value is itself a |
41 | * namespace handle. |
42 | * |
43 | ******************************************************************************/ |
44 | |
45 | acpi_status |
46 | acpi_get_handle(acpi_handle parent, |
47 | const char *pathname, acpi_handle *ret_handle) |
48 | { |
49 | acpi_status status; |
50 | struct acpi_namespace_node *node = NULL; |
51 | struct acpi_namespace_node *prefix_node = NULL; |
52 | |
53 | ACPI_FUNCTION_ENTRY(); |
54 | |
55 | /* Parameter Validation */ |
56 | |
57 | if (!ret_handle || !pathname) { |
58 | return (AE_BAD_PARAMETER); |
59 | } |
60 | |
61 | /* Convert a parent handle to a prefix node */ |
62 | |
63 | if (parent) { |
64 | prefix_node = acpi_ns_validate_handle(handle: parent); |
65 | if (!prefix_node) { |
66 | return (AE_BAD_PARAMETER); |
67 | } |
68 | } |
69 | |
70 | /* |
71 | * Valid cases are: |
72 | * 1) Fully qualified pathname |
73 | * 2) Parent + Relative pathname |
74 | * |
75 | * Error for <null Parent + relative path> |
76 | */ |
77 | if (ACPI_IS_ROOT_PREFIX(pathname[0])) { |
78 | |
79 | /* Pathname is fully qualified (starts with '\') */ |
80 | |
81 | /* Special case for root-only, since we can't search for it */ |
82 | |
83 | if (!strcmp(pathname, ACPI_NS_ROOT_PATH)) { |
84 | *ret_handle = |
85 | ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node); |
86 | return (AE_OK); |
87 | } |
88 | } else if (!prefix_node) { |
89 | |
90 | /* Relative path with null prefix is disallowed */ |
91 | |
92 | return (AE_BAD_PARAMETER); |
93 | } |
94 | |
95 | /* Find the Node and convert to a handle */ |
96 | |
97 | status = |
98 | acpi_ns_get_node(prefix_node, external_pathname: pathname, ACPI_NS_NO_UPSEARCH, out_node: &node); |
99 | if (ACPI_SUCCESS(status)) { |
100 | *ret_handle = ACPI_CAST_PTR(acpi_handle, node); |
101 | } |
102 | |
103 | return (status); |
104 | } |
105 | |
106 | ACPI_EXPORT_SYMBOL(acpi_get_handle) |
107 | |
108 | /****************************************************************************** |
109 | * |
110 | * FUNCTION: acpi_get_name |
111 | * |
112 | * PARAMETERS: handle - Handle to be converted to a pathname |
113 | * name_type - Full pathname or single segment |
114 | * buffer - Buffer for returned path |
115 | * |
116 | * RETURN: Pointer to a string containing the fully qualified Name. |
117 | * |
118 | * DESCRIPTION: This routine returns the fully qualified name associated with |
119 | * the Handle parameter. This and the acpi_pathname_to_handle are |
120 | * complementary functions. |
121 | * |
122 | ******************************************************************************/ |
123 | acpi_status |
124 | acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer *buffer) |
125 | { |
126 | acpi_status status; |
127 | |
128 | /* Parameter validation */ |
129 | |
130 | if (name_type > ACPI_NAME_TYPE_MAX) { |
131 | return (AE_BAD_PARAMETER); |
132 | } |
133 | |
134 | status = acpi_ut_validate_buffer(buffer); |
135 | if (ACPI_FAILURE(status)) { |
136 | return (status); |
137 | } |
138 | |
139 | /* |
140 | * Wants the single segment ACPI name. |
141 | * Validate handle and convert to a namespace Node |
142 | */ |
143 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
144 | if (ACPI_FAILURE(status)) { |
145 | return (status); |
146 | } |
147 | |
148 | if (name_type == ACPI_FULL_PATHNAME || |
149 | name_type == ACPI_FULL_PATHNAME_NO_TRAILING) { |
150 | |
151 | /* Get the full pathname (From the namespace root) */ |
152 | |
153 | status = acpi_ns_handle_to_pathname(target_handle: handle, buffer, |
154 | no_trailing: name_type == |
155 | ACPI_FULL_PATHNAME ? FALSE : |
156 | TRUE); |
157 | } else { |
158 | /* Get the single name */ |
159 | |
160 | status = acpi_ns_handle_to_name(target_handle: handle, buffer); |
161 | } |
162 | |
163 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
164 | return (status); |
165 | } |
166 | |
167 | ACPI_EXPORT_SYMBOL(acpi_get_name) |
168 | |
169 | /****************************************************************************** |
170 | * |
171 | * FUNCTION: acpi_ns_copy_device_id |
172 | * |
173 | * PARAMETERS: dest - Pointer to the destination PNP_DEVICE_ID |
174 | * source - Pointer to the source PNP_DEVICE_ID |
175 | * string_area - Pointer to where to copy the dest string |
176 | * |
177 | * RETURN: Pointer to the next string area |
178 | * |
179 | * DESCRIPTION: Copy a single PNP_DEVICE_ID, including the string data. |
180 | * |
181 | ******************************************************************************/ |
182 | static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest, |
183 | struct acpi_pnp_device_id *source, |
184 | char *string_area) |
185 | { |
186 | /* Create the destination PNP_DEVICE_ID */ |
187 | |
188 | dest->string = string_area; |
189 | dest->length = source->length; |
190 | |
191 | /* Copy actual string and return a pointer to the next string area */ |
192 | |
193 | memcpy(string_area, source->string, source->length); |
194 | return (string_area + source->length); |
195 | } |
196 | |
197 | /****************************************************************************** |
198 | * |
199 | * FUNCTION: acpi_get_object_info |
200 | * |
201 | * PARAMETERS: handle - Object Handle |
202 | * return_buffer - Where the info is returned |
203 | * |
204 | * RETURN: Status |
205 | * |
206 | * DESCRIPTION: Returns information about an object as gleaned from the |
207 | * namespace node and possibly by running several standard |
208 | * control methods (Such as in the case of a device.) |
209 | * |
210 | * For Device and Processor objects, run the Device _HID, _UID, _CID, |
211 | * _CLS, _ADR, _sx_w, and _sx_d methods. |
212 | * |
213 | * Note: Allocates the return buffer, must be freed by the caller. |
214 | * |
215 | * Note: This interface is intended to be used during the initial device |
216 | * discovery namespace traversal. Therefore, no complex methods can be |
217 | * executed, especially those that access operation regions. Therefore, do |
218 | * not add any additional methods that could cause problems in this area. |
219 | * Because of this reason support for the following methods has been removed: |
220 | * 1) _SUB method was removed (11/2015) |
221 | * 2) _STA method was removed (02/2018) |
222 | * |
223 | ******************************************************************************/ |
224 | |
225 | acpi_status |
226 | acpi_get_object_info(acpi_handle handle, |
227 | struct acpi_device_info **return_buffer) |
228 | { |
229 | struct acpi_namespace_node *node; |
230 | struct acpi_device_info *info; |
231 | struct acpi_pnp_device_id_list *cid_list = NULL; |
232 | struct acpi_pnp_device_id *hid = NULL; |
233 | struct acpi_pnp_device_id *uid = NULL; |
234 | struct acpi_pnp_device_id *cls = NULL; |
235 | char *next_id_string; |
236 | acpi_object_type type; |
237 | acpi_name name; |
238 | u8 param_count = 0; |
239 | u16 valid = 0; |
240 | u32 info_size; |
241 | u32 i; |
242 | acpi_status status; |
243 | |
244 | /* Parameter validation */ |
245 | |
246 | if (!handle || !return_buffer) { |
247 | return (AE_BAD_PARAMETER); |
248 | } |
249 | |
250 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
251 | if (ACPI_FAILURE(status)) { |
252 | return (status); |
253 | } |
254 | |
255 | node = acpi_ns_validate_handle(handle); |
256 | if (!node) { |
257 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
258 | return (AE_BAD_PARAMETER); |
259 | } |
260 | |
261 | /* Get the namespace node data while the namespace is locked */ |
262 | |
263 | info_size = sizeof(struct acpi_device_info); |
264 | type = node->type; |
265 | name = node->name.integer; |
266 | |
267 | if (node->type == ACPI_TYPE_METHOD) { |
268 | param_count = node->object->method.param_count; |
269 | } |
270 | |
271 | status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
272 | if (ACPI_FAILURE(status)) { |
273 | return (status); |
274 | } |
275 | |
276 | if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { |
277 | /* |
278 | * Get extra info for ACPI Device/Processor objects only: |
279 | * Run the Device _HID, _UID, _CLS, and _CID methods. |
280 | * |
281 | * Note: none of these methods are required, so they may or may |
282 | * not be present for this device. The Info->Valid bitfield is used |
283 | * to indicate which methods were found and run successfully. |
284 | */ |
285 | |
286 | /* Execute the Device._HID method */ |
287 | |
288 | status = acpi_ut_execute_HID(device_node: node, return_id: &hid); |
289 | if (ACPI_SUCCESS(status)) { |
290 | info_size += hid->length; |
291 | valid |= ACPI_VALID_HID; |
292 | } |
293 | |
294 | /* Execute the Device._UID method */ |
295 | |
296 | status = acpi_ut_execute_UID(device_node: node, return_id: &uid); |
297 | if (ACPI_SUCCESS(status)) { |
298 | info_size += uid->length; |
299 | valid |= ACPI_VALID_UID; |
300 | } |
301 | |
302 | /* Execute the Device._CID method */ |
303 | |
304 | status = acpi_ut_execute_CID(device_node: node, return_cid_list: &cid_list); |
305 | if (ACPI_SUCCESS(status)) { |
306 | |
307 | /* Add size of CID strings and CID pointer array */ |
308 | |
309 | info_size += |
310 | (cid_list->list_size - |
311 | sizeof(struct acpi_pnp_device_id_list)); |
312 | valid |= ACPI_VALID_CID; |
313 | } |
314 | |
315 | /* Execute the Device._CLS method */ |
316 | |
317 | status = acpi_ut_execute_CLS(device_node: node, return_id: &cls); |
318 | if (ACPI_SUCCESS(status)) { |
319 | info_size += cls->length; |
320 | valid |= ACPI_VALID_CLS; |
321 | } |
322 | } |
323 | |
324 | /* |
325 | * Now that we have the variable-length data, we can allocate the |
326 | * return buffer |
327 | */ |
328 | info = ACPI_ALLOCATE_ZEROED(info_size); |
329 | if (!info) { |
330 | status = AE_NO_MEMORY; |
331 | goto cleanup; |
332 | } |
333 | |
334 | /* Get the fixed-length data */ |
335 | |
336 | if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { |
337 | /* |
338 | * Get extra info for ACPI Device/Processor objects only: |
339 | * Run the _ADR and, sx_w, and _sx_d methods. |
340 | * |
341 | * Notes: none of these methods are required, so they may or may |
342 | * not be present for this device. The Info->Valid bitfield is used |
343 | * to indicate which methods were found and run successfully. |
344 | */ |
345 | |
346 | /* Execute the Device._ADR method */ |
347 | |
348 | status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, device_node: node, |
349 | value: &info->address); |
350 | if (ACPI_SUCCESS(status)) { |
351 | valid |= ACPI_VALID_ADR; |
352 | } |
353 | |
354 | /* Execute the Device._sx_w methods */ |
355 | |
356 | status = acpi_ut_execute_power_methods(device_node: node, |
357 | method_names: acpi_gbl_lowest_dstate_names, |
358 | ACPI_NUM_sx_w_METHODS, |
359 | out_values: info->lowest_dstates); |
360 | if (ACPI_SUCCESS(status)) { |
361 | valid |= ACPI_VALID_SXWS; |
362 | } |
363 | |
364 | /* Execute the Device._sx_d methods */ |
365 | |
366 | status = acpi_ut_execute_power_methods(device_node: node, |
367 | method_names: acpi_gbl_highest_dstate_names, |
368 | ACPI_NUM_sx_d_METHODS, |
369 | out_values: info->highest_dstates); |
370 | if (ACPI_SUCCESS(status)) { |
371 | valid |= ACPI_VALID_SXDS; |
372 | } |
373 | } |
374 | |
375 | /* |
376 | * Create a pointer to the string area of the return buffer. |
377 | * Point to the end of the base struct acpi_device_info structure. |
378 | */ |
379 | next_id_string = ACPI_CAST_PTR(char, info->compatible_id_list.ids); |
380 | if (cid_list) { |
381 | |
382 | /* Point past the CID PNP_DEVICE_ID array */ |
383 | |
384 | next_id_string += |
385 | ((acpi_size)cid_list->count * |
386 | sizeof(struct acpi_pnp_device_id)); |
387 | } |
388 | |
389 | /* |
390 | * Copy the HID, UID, and CIDs to the return buffer. The variable-length |
391 | * strings are copied to the reserved area at the end of the buffer. |
392 | * |
393 | * For HID and CID, check if the ID is a PCI Root Bridge. |
394 | */ |
395 | if (hid) { |
396 | next_id_string = acpi_ns_copy_device_id(dest: &info->hardware_id, |
397 | source: hid, string_area: next_id_string); |
398 | |
399 | if (acpi_ut_is_pci_root_bridge(id: hid->string)) { |
400 | info->flags |= ACPI_PCI_ROOT_BRIDGE; |
401 | } |
402 | } |
403 | |
404 | if (uid) { |
405 | next_id_string = acpi_ns_copy_device_id(dest: &info->unique_id, |
406 | source: uid, string_area: next_id_string); |
407 | } |
408 | |
409 | if (cid_list) { |
410 | info->compatible_id_list.count = cid_list->count; |
411 | info->compatible_id_list.list_size = cid_list->list_size; |
412 | |
413 | /* Copy each CID */ |
414 | |
415 | for (i = 0; i < cid_list->count; i++) { |
416 | next_id_string = |
417 | acpi_ns_copy_device_id(dest: &info->compatible_id_list. |
418 | ids[i], source: &cid_list->ids[i], |
419 | string_area: next_id_string); |
420 | |
421 | if (acpi_ut_is_pci_root_bridge(id: cid_list->ids[i].string)) { |
422 | info->flags |= ACPI_PCI_ROOT_BRIDGE; |
423 | } |
424 | } |
425 | } |
426 | |
427 | if (cls) { |
428 | (void)acpi_ns_copy_device_id(dest: &info->class_code, |
429 | source: cls, string_area: next_id_string); |
430 | } |
431 | |
432 | /* Copy the fixed-length data */ |
433 | |
434 | info->info_size = info_size; |
435 | info->type = type; |
436 | info->name = name; |
437 | info->param_count = param_count; |
438 | info->valid = valid; |
439 | |
440 | *return_buffer = info; |
441 | status = AE_OK; |
442 | |
443 | cleanup: |
444 | if (hid) { |
445 | ACPI_FREE(hid); |
446 | } |
447 | if (uid) { |
448 | ACPI_FREE(uid); |
449 | } |
450 | if (cid_list) { |
451 | ACPI_FREE(cid_list); |
452 | } |
453 | if (cls) { |
454 | ACPI_FREE(cls); |
455 | } |
456 | return (status); |
457 | } |
458 | |
459 | ACPI_EXPORT_SYMBOL(acpi_get_object_info) |
460 | |
461 | /****************************************************************************** |
462 | * |
463 | * FUNCTION: acpi_install_method |
464 | * |
465 | * PARAMETERS: buffer - An ACPI table containing one control method |
466 | * |
467 | * RETURN: Status |
468 | * |
469 | * DESCRIPTION: Install a control method into the namespace. If the method |
470 | * name already exists in the namespace, it is overwritten. The |
471 | * input buffer must contain a valid DSDT or SSDT containing a |
472 | * single control method. |
473 | * |
474 | ******************************************************************************/ |
475 | acpi_status acpi_install_method(u8 *buffer) |
476 | { |
477 | struct acpi_table_header *table = |
478 | ACPI_CAST_PTR(struct acpi_table_header, buffer); |
479 | u8 *aml_buffer; |
480 | u8 *aml_start; |
481 | char *path; |
482 | struct acpi_namespace_node *node; |
483 | union acpi_operand_object *method_obj; |
484 | struct acpi_parse_state parser_state; |
485 | u32 aml_length; |
486 | u16 opcode; |
487 | u8 method_flags; |
488 | acpi_status status; |
489 | |
490 | /* Parameter validation */ |
491 | |
492 | if (!buffer) { |
493 | return (AE_BAD_PARAMETER); |
494 | } |
495 | |
496 | /* Table must be a DSDT or SSDT */ |
497 | |
498 | if (!ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_DSDT) && |
499 | !ACPI_COMPARE_NAMESEG(table->signature, ACPI_SIG_SSDT)) { |
500 | return (AE_BAD_HEADER); |
501 | } |
502 | |
503 | /* First AML opcode in the table must be a control method */ |
504 | |
505 | parser_state.aml = buffer + sizeof(struct acpi_table_header); |
506 | opcode = acpi_ps_peek_opcode(state: &parser_state); |
507 | if (opcode != AML_METHOD_OP) { |
508 | return (AE_BAD_PARAMETER); |
509 | } |
510 | |
511 | /* Extract method information from the raw AML */ |
512 | |
513 | parser_state.aml += acpi_ps_get_opcode_size(opcode); |
514 | parser_state.pkg_end = acpi_ps_get_next_package_end(parser_state: &parser_state); |
515 | path = acpi_ps_get_next_namestring(parser_state: &parser_state); |
516 | |
517 | method_flags = *parser_state.aml++; |
518 | aml_start = parser_state.aml; |
519 | aml_length = (u32)ACPI_PTR_DIFF(parser_state.pkg_end, aml_start); |
520 | |
521 | /* |
522 | * Allocate resources up-front. We don't want to have to delete a new |
523 | * node from the namespace if we cannot allocate memory. |
524 | */ |
525 | aml_buffer = ACPI_ALLOCATE(aml_length); |
526 | if (!aml_buffer) { |
527 | return (AE_NO_MEMORY); |
528 | } |
529 | |
530 | method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); |
531 | if (!method_obj) { |
532 | ACPI_FREE(aml_buffer); |
533 | return (AE_NO_MEMORY); |
534 | } |
535 | |
536 | /* Lock namespace for acpi_ns_lookup, we may be creating a new node */ |
537 | |
538 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
539 | if (ACPI_FAILURE(status)) { |
540 | goto error_exit; |
541 | } |
542 | |
543 | /* The lookup either returns an existing node or creates a new one */ |
544 | |
545 | status = |
546 | acpi_ns_lookup(NULL, name: path, ACPI_TYPE_METHOD, interpreter_mode: ACPI_IMODE_LOAD_PASS1, |
547 | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, |
548 | NULL, ret_node: &node); |
549 | |
550 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
551 | |
552 | if (ACPI_FAILURE(status)) { /* ns_lookup */ |
553 | if (status != AE_ALREADY_EXISTS) { |
554 | goto error_exit; |
555 | } |
556 | |
557 | /* Node existed previously, make sure it is a method node */ |
558 | |
559 | if (node->type != ACPI_TYPE_METHOD) { |
560 | status = AE_TYPE; |
561 | goto error_exit; |
562 | } |
563 | } |
564 | |
565 | /* Copy the method AML to the local buffer */ |
566 | |
567 | memcpy(aml_buffer, aml_start, aml_length); |
568 | |
569 | /* Initialize the method object with the new method's information */ |
570 | |
571 | method_obj->method.aml_start = aml_buffer; |
572 | method_obj->method.aml_length = aml_length; |
573 | |
574 | method_obj->method.param_count = (u8) |
575 | (method_flags & AML_METHOD_ARG_COUNT); |
576 | |
577 | if (method_flags & AML_METHOD_SERIALIZED) { |
578 | method_obj->method.info_flags = ACPI_METHOD_SERIALIZED; |
579 | |
580 | method_obj->method.sync_level = (u8) |
581 | ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); |
582 | } |
583 | |
584 | /* |
585 | * Now that it is complete, we can attach the new method object to |
586 | * the method Node (detaches/deletes any existing object) |
587 | */ |
588 | status = acpi_ns_attach_object(node, object: method_obj, ACPI_TYPE_METHOD); |
589 | |
590 | /* |
591 | * Flag indicates AML buffer is dynamic, must be deleted later. |
592 | * Must be set only after attach above. |
593 | */ |
594 | node->flags |= ANOBJ_ALLOCATED_BUFFER; |
595 | |
596 | /* Remove local reference to the method object */ |
597 | |
598 | acpi_ut_remove_reference(object: method_obj); |
599 | return (status); |
600 | |
601 | error_exit: |
602 | |
603 | ACPI_FREE(aml_buffer); |
604 | ACPI_FREE(method_obj); |
605 | return (status); |
606 | } |
607 | ACPI_EXPORT_SYMBOL(acpi_install_method) |
608 | |