| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
| /****************************************************************************** |
| * |
| * Module Name: nsrepair2 - Repair for objects returned by specific |
| * predefined methods |
| * |
| * Copyright (C) 2000 - 2019, Intel Corp. |
| * |
| *****************************************************************************/ |
| |
| #include <acpi/acpi.h> |
| #include "accommon.h" |
| #include "acnamesp.h" |
| |
| #define _COMPONENT ACPI_NAMESPACE |
| ACPI_MODULE_NAME("nsrepair2") |
| |
| /* |
| * Information structure and handler for ACPI predefined names that can |
| * be repaired on a per-name basis. |
| */ |
| typedef |
| acpi_status (*acpi_repair_function) (struct acpi_evaluate_info * info, |
| union acpi_operand_object ** |
| return_object_ptr); |
| |
| typedef struct acpi_repair_info { |
| char name[ACPI_NAME_SIZE]; |
| acpi_repair_function repair_function; |
| |
| } acpi_repair_info; |
| |
| /* Local prototypes */ |
| |
| static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct |
| acpi_namespace_node |
| *node); |
| |
| static acpi_status |
| acpi_ns_repair_ALR(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_CID(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_CST(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_FDE(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_HID(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_PRT(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_PSS(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_repair_TSS(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr); |
| |
| static acpi_status |
| acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, |
| union acpi_operand_object *return_object, |
| u32 start_index, |
| u32 expected_count, |
| u32 sort_index, |
| u8 sort_direction, char *sort_key_name); |
| |
| /* Values for sort_direction above */ |
| |
| #define ACPI_SORT_ASCENDING 0 |
| #define ACPI_SORT_DESCENDING 1 |
| |
| static void |
| acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index); |
| |
| static void |
| acpi_ns_sort_list(union acpi_operand_object **elements, |
| u32 count, u32 index, u8 sort_direction); |
| |
| /* |
| * This table contains the names of the predefined methods for which we can |
| * perform more complex repairs. |
| * |
| * As necessary: |
| * |
| * _ALR: Sort the list ascending by ambient_illuminance |
| * _CID: Strings: uppercase all, remove any leading asterisk |
| * _CST: Sort the list ascending by C state type |
| * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs |
| * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs |
| * _HID: Strings: uppercase all, remove any leading asterisk |
| * _PRT: Fix reversed source_name and source_index |
| * _PSS: Sort the list descending by Power |
| * _TSS: Sort the list descending by Power |
| * |
| * Names that must be packages, but cannot be sorted: |
| * |
| * _BCL: Values are tied to the Package index where they appear, and cannot |
| * be moved or sorted. These index values are used for _BQC and _BCM. |
| * However, we can fix the case where a buffer is returned, by converting |
| * it to a Package of integers. |
| */ |
| static const struct acpi_repair_info acpi_ns_repairable_names[] = { |
| {"_ALR", acpi_ns_repair_ALR}, |
| {"_CID", acpi_ns_repair_CID}, |
| {"_CST", acpi_ns_repair_CST}, |
| {"_FDE", acpi_ns_repair_FDE}, |
| {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ |
| {"_HID", acpi_ns_repair_HID}, |
| {"_PRT", acpi_ns_repair_PRT}, |
| {"_PSS", acpi_ns_repair_PSS}, |
| {"_TSS", acpi_ns_repair_TSS}, |
| {{0, 0, 0, 0}, NULL} /* Table terminator */ |
| }; |
| |
| #define ACPI_FDE_FIELD_COUNT 5 |
| #define ACPI_FDE_BYTE_BUFFER_SIZE 5 |
| #define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32)) |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_complex_repairs |
| * |
| * PARAMETERS: info - Method execution information block |
| * node - Namespace node for the method/object |
| * validate_status - Original status of earlier validation |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if repair was successful. If name is not |
| * matched, validate_status is returned. |
| * |
| * DESCRIPTION: Attempt to repair/convert a return object of a type that was |
| * not expected. |
| * |
| *****************************************************************************/ |
| |
| acpi_status |
| acpi_ns_complex_repairs(struct acpi_evaluate_info *info, |
| struct acpi_namespace_node *node, |
| acpi_status validate_status, |
| union acpi_operand_object **return_object_ptr) |
| { |
| const struct acpi_repair_info *predefined; |
| acpi_status status; |
| |
| /* Check if this name is in the list of repairable names */ |
| |
| predefined = acpi_ns_match_complex_repair(node); |
| if (!predefined) { |
| return (validate_status); |
| } |
| |
| status = predefined->repair_function(info, return_object_ptr); |
| return (status); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_match_complex_repair |
| * |
| * PARAMETERS: node - Namespace node for the method/object |
| * |
| * RETURN: Pointer to entry in repair table. NULL indicates not found. |
| * |
| * DESCRIPTION: Check an object name against the repairable object list. |
| * |
| *****************************************************************************/ |
| |
| static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct |
| acpi_namespace_node |
| *node) |
| { |
| const struct acpi_repair_info *this_name; |
| |
| /* Search info table for a repairable predefined method/object name */ |
| |
| this_name = acpi_ns_repairable_names; |
| while (this_name->repair_function) { |
| if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) { |
| return (this_name); |
| } |
| |
| this_name++; |
| } |
| |
| return (NULL); /* Not found */ |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_ALR |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list |
| * ascending by the ambient illuminance values. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_ALR(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| acpi_status status; |
| |
| status = acpi_ns_check_sorted_list(info, return_object, 0, 2, 1, |
| ACPI_SORT_ASCENDING, |
| "AmbientIlluminance"); |
| |
| return (status); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_FDE |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return |
| * value is a Buffer of 5 DWORDs. This function repairs a common |
| * problem where the return value is a Buffer of BYTEs, not |
| * DWORDs. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_FDE(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| union acpi_operand_object *buffer_object; |
| u8 *byte_buffer; |
| u32 *dword_buffer; |
| u32 i; |
| |
| ACPI_FUNCTION_NAME(ns_repair_FDE); |
| |
| switch (return_object->common.type) { |
| case ACPI_TYPE_BUFFER: |
| |
| /* This is the expected type. Length should be (at least) 5 DWORDs */ |
| |
| if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) { |
| return (AE_OK); |
| } |
| |
| /* We can only repair if we have exactly 5 BYTEs */ |
| |
| if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) { |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, |
| info->node_flags, |
| "Incorrect return buffer length %u, expected %u", |
| return_object->buffer.length, |
| ACPI_FDE_DWORD_BUFFER_SIZE)); |
| |
| return (AE_AML_OPERAND_TYPE); |
| } |
| |
| /* Create the new (larger) buffer object */ |
| |
| buffer_object = |
| acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE); |
| if (!buffer_object) { |
| return (AE_NO_MEMORY); |
| } |
| |
| /* Expand each byte to a DWORD */ |
| |
| byte_buffer = return_object->buffer.pointer; |
| dword_buffer = ACPI_CAST_PTR(u32, |
| buffer_object->buffer.pointer); |
| |
| for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) { |
| *dword_buffer = (u32) *byte_buffer; |
| dword_buffer++; |
| byte_buffer++; |
| } |
| |
| ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, |
| "%s Expanded Byte Buffer to expected DWord Buffer\n", |
| info->full_pathname)); |
| break; |
| |
| default: |
| |
| return (AE_AML_OPERAND_TYPE); |
| } |
| |
| /* Delete the original return object, return the new buffer object */ |
| |
| acpi_ut_remove_reference(return_object); |
| *return_object_ptr = buffer_object; |
| |
| info->return_flags |= ACPI_OBJECT_REPAIRED; |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_CID |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _CID object. If a string, ensure that all |
| * letters are uppercase and that there is no leading asterisk. |
| * If a Package, ensure same for all string elements. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_CID(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| acpi_status status; |
| union acpi_operand_object *return_object = *return_object_ptr; |
| union acpi_operand_object **element_ptr; |
| union acpi_operand_object *original_element; |
| u16 original_ref_count; |
| u32 i; |
| |
| /* Check for _CID as a simple string */ |
| |
| if (return_object->common.type == ACPI_TYPE_STRING) { |
| status = acpi_ns_repair_HID(info, return_object_ptr); |
| return (status); |
| } |
| |
| /* Exit if not a Package */ |
| |
| if (return_object->common.type != ACPI_TYPE_PACKAGE) { |
| return (AE_OK); |
| } |
| |
| /* Examine each element of the _CID package */ |
| |
| element_ptr = return_object->package.elements; |
| for (i = 0; i < return_object->package.count; i++) { |
| original_element = *element_ptr; |
| original_ref_count = original_element->common.reference_count; |
| |
| status = acpi_ns_repair_HID(info, element_ptr); |
| if (ACPI_FAILURE(status)) { |
| return (status); |
| } |
| |
| if (original_element != *element_ptr) { |
| |
| /* Update reference count of new object */ |
| |
| (*element_ptr)->common.reference_count = |
| original_ref_count; |
| } |
| |
| element_ptr++; |
| } |
| |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_CST |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _CST object: |
| * 1. Sort the list ascending by C state type |
| * 2. Ensure type cannot be zero |
| * 3. A subpackage count of zero means _CST is meaningless |
| * 4. Count must match the number of C state subpackages |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_CST(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| union acpi_operand_object **outer_elements; |
| u32 outer_element_count; |
| union acpi_operand_object *obj_desc; |
| acpi_status status; |
| u8 removing; |
| u32 i; |
| |
| ACPI_FUNCTION_NAME(ns_repair_CST); |
| |
| /* |
| * Check if the C-state type values are proportional. |
| */ |
| outer_element_count = return_object->package.count - 1; |
| i = 0; |
| while (i < outer_element_count) { |
| outer_elements = &return_object->package.elements[i + 1]; |
| removing = FALSE; |
| |
| if ((*outer_elements)->package.count == 0) { |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, |
| info->node_flags, |
| "SubPackage[%u] - removing entry due to zero count", |
| i)); |
| removing = TRUE; |
| goto remove_element; |
| } |
| |
| obj_desc = (*outer_elements)->package.elements[1]; /* Index1 = Type */ |
| if ((u32)obj_desc->integer.value == 0) { |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, |
| info->node_flags, |
| "SubPackage[%u] - removing entry due to invalid Type(0)", |
| i)); |
| removing = TRUE; |
| } |
| |
| remove_element: |
| if (removing) { |
| acpi_ns_remove_element(return_object, i + 1); |
| outer_element_count--; |
| } else { |
| i++; |
| } |
| } |
| |
| /* Update top-level package count, Type "Integer" checked elsewhere */ |
| |
| obj_desc = return_object->package.elements[0]; |
| obj_desc->integer.value = outer_element_count; |
| |
| /* |
| * Entries (subpackages) in the _CST Package must be sorted by the |
| * C-state type, in ascending order. |
| */ |
| status = acpi_ns_check_sorted_list(info, return_object, 1, 4, 1, |
| ACPI_SORT_ASCENDING, "C-State Type"); |
| if (ACPI_FAILURE(status)) { |
| return (status); |
| } |
| |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_HID |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _HID object. If a string, ensure that all |
| * letters are uppercase and that there is no leading asterisk. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_HID(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| union acpi_operand_object *new_string; |
| char *source; |
| char *dest; |
| |
| ACPI_FUNCTION_NAME(ns_repair_HID); |
| |
| /* We only care about string _HID objects (not integers) */ |
| |
| if (return_object->common.type != ACPI_TYPE_STRING) { |
| return (AE_OK); |
| } |
| |
| if (return_object->string.length == 0) { |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, info->node_flags, |
| "Invalid zero-length _HID or _CID string")); |
| |
| /* Return AE_OK anyway, let driver handle it */ |
| |
| info->return_flags |= ACPI_OBJECT_REPAIRED; |
| return (AE_OK); |
| } |
| |
| /* It is simplest to always create a new string object */ |
| |
| new_string = acpi_ut_create_string_object(return_object->string.length); |
| if (!new_string) { |
| return (AE_NO_MEMORY); |
| } |
| |
| /* |
| * Remove a leading asterisk if present. For some unknown reason, there |
| * are many machines in the field that contains IDs like this. |
| * |
| * Examples: "*PNP0C03", "*ACPI0003" |
| */ |
| source = return_object->string.pointer; |
| if (*source == '*') { |
| source++; |
| new_string->string.length--; |
| |
| ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, |
| "%s: Removed invalid leading asterisk\n", |
| info->full_pathname)); |
| } |
| |
| /* |
| * Copy and uppercase the string. From the ACPI 5.0 specification: |
| * |
| * A valid PNP ID must be of the form "AAA####" where A is an uppercase |
| * letter and # is a hex digit. A valid ACPI ID must be of the form |
| * "NNNN####" where N is an uppercase letter or decimal digit, and |
| * # is a hex digit. |
| */ |
| for (dest = new_string->string.pointer; *source; dest++, source++) { |
| *dest = (char)toupper((int)*source); |
| } |
| |
| acpi_ut_remove_reference(return_object); |
| *return_object_ptr = new_string; |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_PRT |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _PRT object. If necessary, fix reversed |
| * source_name and source_index field, a common BIOS bug. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_PRT(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *package_object = *return_object_ptr; |
| union acpi_operand_object **top_object_list; |
| union acpi_operand_object **sub_object_list; |
| union acpi_operand_object *obj_desc; |
| union acpi_operand_object *sub_package; |
| u32 element_count; |
| u32 index; |
| |
| /* Each element in the _PRT package is a subpackage */ |
| |
| top_object_list = package_object->package.elements; |
| element_count = package_object->package.count; |
| |
| /* Examine each subpackage */ |
| |
| for (index = 0; index < element_count; index++, top_object_list++) { |
| sub_package = *top_object_list; |
| sub_object_list = sub_package->package.elements; |
| |
| /* Check for minimum required element count */ |
| |
| if (sub_package->package.count < 4) { |
| continue; |
| } |
| |
| /* |
| * If the BIOS has erroneously reversed the _PRT source_name (index 2) |
| * and the source_index (index 3), fix it. _PRT is important enough to |
| * workaround this BIOS error. This also provides compatibility with |
| * other ACPI implementations. |
| */ |
| obj_desc = sub_object_list[3]; |
| if (!obj_desc || (obj_desc->common.type != ACPI_TYPE_INTEGER)) { |
| sub_object_list[3] = sub_object_list[2]; |
| sub_object_list[2] = obj_desc; |
| info->return_flags |= ACPI_OBJECT_REPAIRED; |
| |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, |
| info->node_flags, |
| "PRT[%X]: Fixed reversed SourceName and SourceIndex", |
| index)); |
| } |
| } |
| |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_PSS |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list |
| * by the CPU frequencies. Check that the power dissipation values |
| * are all proportional to CPU frequency (i.e., sorting by |
| * frequency should be the same as sorting by power.) |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_PSS(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| union acpi_operand_object **outer_elements; |
| u32 outer_element_count; |
| union acpi_operand_object **elements; |
| union acpi_operand_object *obj_desc; |
| u32 previous_value; |
| acpi_status status; |
| u32 i; |
| |
| /* |
| * Entries (subpackages) in the _PSS Package must be sorted by power |
| * dissipation, in descending order. If it appears that the list is |
| * incorrectly sorted, sort it. We sort by cpu_frequency, since this |
| * should be proportional to the power. |
| */ |
| status = acpi_ns_check_sorted_list(info, return_object, 0, 6, 0, |
| ACPI_SORT_DESCENDING, |
| "CpuFrequency"); |
| if (ACPI_FAILURE(status)) { |
| return (status); |
| } |
| |
| /* |
| * We now know the list is correctly sorted by CPU frequency. Check if |
| * the power dissipation values are proportional. |
| */ |
| previous_value = ACPI_UINT32_MAX; |
| outer_elements = return_object->package.elements; |
| outer_element_count = return_object->package.count; |
| |
| for (i = 0; i < outer_element_count; i++) { |
| elements = (*outer_elements)->package.elements; |
| obj_desc = elements[1]; /* Index1 = power_dissipation */ |
| |
| if ((u32)obj_desc->integer.value > previous_value) { |
| ACPI_WARN_PREDEFINED((AE_INFO, |
| info->full_pathname, |
| info->node_flags, |
| "SubPackage[%u,%u] - suspicious power dissipation values", |
| i - 1, i)); |
| } |
| |
| previous_value = (u32) obj_desc->integer.value; |
| outer_elements++; |
| } |
| |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_repair_TSS |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object_ptr - Pointer to the object returned from the |
| * evaluation of a method or object |
| * |
| * RETURN: Status. AE_OK if object is OK or was repaired successfully |
| * |
| * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list |
| * descending by the power dissipation values. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_repair_TSS(struct acpi_evaluate_info *info, |
| union acpi_operand_object **return_object_ptr) |
| { |
| union acpi_operand_object *return_object = *return_object_ptr; |
| acpi_status status; |
| struct acpi_namespace_node *node; |
| |
| /* |
| * We can only sort the _TSS return package if there is no _PSS in the |
| * same scope. This is because if _PSS is present, the ACPI specification |
| * dictates that the _TSS Power Dissipation field is to be ignored, and |
| * therefore some BIOSs leave garbage values in the _TSS Power field(s). |
| * In this case, it is best to just return the _TSS package as-is. |
| * (May, 2011) |
| */ |
| status = acpi_ns_get_node(info->node, "^_PSS", |
| ACPI_NS_NO_UPSEARCH, &node); |
| if (ACPI_SUCCESS(status)) { |
| return (AE_OK); |
| } |
| |
| status = acpi_ns_check_sorted_list(info, return_object, 0, 5, 1, |
| ACPI_SORT_DESCENDING, |
| "PowerDissipation"); |
| |
| return (status); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_check_sorted_list |
| * |
| * PARAMETERS: info - Method execution information block |
| * return_object - Pointer to the top-level returned object |
| * start_index - Index of the first subpackage |
| * expected_count - Minimum length of each subpackage |
| * sort_index - Subpackage entry to sort on |
| * sort_direction - Ascending or descending |
| * sort_key_name - Name of the sort_index field |
| * |
| * RETURN: Status. AE_OK if the list is valid and is sorted correctly or |
| * has been repaired by sorting the list. |
| * |
| * DESCRIPTION: Check if the package list is valid and sorted correctly by the |
| * sort_index. If not, then sort the list. |
| * |
| *****************************************************************************/ |
| |
| static acpi_status |
| acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, |
| union acpi_operand_object *return_object, |
| u32 start_index, |
| u32 expected_count, |
| u32 sort_index, |
| u8 sort_direction, char *sort_key_name) |
| { |
| u32 outer_element_count; |
| union acpi_operand_object **outer_elements; |
| union acpi_operand_object **elements; |
| union acpi_operand_object *obj_desc; |
| u32 i; |
| u32 previous_value; |
| |
| ACPI_FUNCTION_NAME(ns_check_sorted_list); |
| |
| /* The top-level object must be a package */ |
| |
| if (return_object->common.type != ACPI_TYPE_PACKAGE) { |
| return (AE_AML_OPERAND_TYPE); |
| } |
| |
| /* |
| * NOTE: assumes list of subpackages contains no NULL elements. |
| * Any NULL elements should have been removed by earlier call |
| * to acpi_ns_remove_null_elements. |
| */ |
| outer_element_count = return_object->package.count; |
| if (!outer_element_count || start_index >= outer_element_count) { |
| return (AE_AML_PACKAGE_LIMIT); |
| } |
| |
| outer_elements = &return_object->package.elements[start_index]; |
| outer_element_count -= start_index; |
| |
| previous_value = 0; |
| if (sort_direction == ACPI_SORT_DESCENDING) { |
| previous_value = ACPI_UINT32_MAX; |
| } |
| |
| /* Examine each subpackage */ |
| |
| for (i = 0; i < outer_element_count; i++) { |
| |
| /* Each element of the top-level package must also be a package */ |
| |
| if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) { |
| return (AE_AML_OPERAND_TYPE); |
| } |
| |
| /* Each subpackage must have the minimum length */ |
| |
| if ((*outer_elements)->package.count < expected_count) { |
| return (AE_AML_PACKAGE_LIMIT); |
| } |
| |
| elements = (*outer_elements)->package.elements; |
| obj_desc = elements[sort_index]; |
| |
| if (obj_desc->common.type != ACPI_TYPE_INTEGER) { |
| return (AE_AML_OPERAND_TYPE); |
| } |
| |
| /* |
| * The list must be sorted in the specified order. If we detect a |
| * discrepancy, sort the entire list. |
| */ |
| if (((sort_direction == ACPI_SORT_ASCENDING) && |
| (obj_desc->integer.value < previous_value)) || |
| ((sort_direction == ACPI_SORT_DESCENDING) && |
| (obj_desc->integer.value > previous_value))) { |
| acpi_ns_sort_list(&return_object->package. |
| elements[start_index], |
| outer_element_count, sort_index, |
| sort_direction); |
| |
| info->return_flags |= ACPI_OBJECT_REPAIRED; |
| |
| ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, |
| "%s: Repaired unsorted list - now sorted by %s\n", |
| info->full_pathname, sort_key_name)); |
| return (AE_OK); |
| } |
| |
| previous_value = (u32) obj_desc->integer.value; |
| outer_elements++; |
| } |
| |
| return (AE_OK); |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_sort_list |
| * |
| * PARAMETERS: elements - Package object element list |
| * count - Element count for above |
| * index - Sort by which package element |
| * sort_direction - Ascending or Descending sort |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Sort the objects that are in a package element list. |
| * |
| * NOTE: Assumes that all NULL elements have been removed from the package, |
| * and that all elements have been verified to be of type Integer. |
| * |
| *****************************************************************************/ |
| |
| static void |
| acpi_ns_sort_list(union acpi_operand_object **elements, |
| u32 count, u32 index, u8 sort_direction) |
| { |
| union acpi_operand_object *obj_desc1; |
| union acpi_operand_object *obj_desc2; |
| union acpi_operand_object *temp_obj; |
| u32 i; |
| u32 j; |
| |
| /* Simple bubble sort */ |
| |
| for (i = 1; i < count; i++) { |
| for (j = (count - 1); j >= i; j--) { |
| obj_desc1 = elements[j - 1]->package.elements[index]; |
| obj_desc2 = elements[j]->package.elements[index]; |
| |
| if (((sort_direction == ACPI_SORT_ASCENDING) && |
| (obj_desc1->integer.value > |
| obj_desc2->integer.value)) |
| || ((sort_direction == ACPI_SORT_DESCENDING) |
| && (obj_desc1->integer.value < |
| obj_desc2->integer.value))) { |
| temp_obj = elements[j - 1]; |
| elements[j - 1] = elements[j]; |
| elements[j] = temp_obj; |
| } |
| } |
| } |
| } |
| |
| /****************************************************************************** |
| * |
| * FUNCTION: acpi_ns_remove_element |
| * |
| * PARAMETERS: obj_desc - Package object element list |
| * index - Index of element to remove |
| * |
| * RETURN: None |
| * |
| * DESCRIPTION: Remove the requested element of a package and delete it. |
| * |
| *****************************************************************************/ |
| |
| static void |
| acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index) |
| { |
| union acpi_operand_object **source; |
| union acpi_operand_object **dest; |
| u32 count; |
| u32 new_count; |
| u32 i; |
| |
| ACPI_FUNCTION_NAME(ns_remove_element); |
| |
| count = obj_desc->package.count; |
| new_count = count - 1; |
| |
| source = obj_desc->package.elements; |
| dest = source; |
| |
| /* Examine all elements of the package object, remove matched index */ |
| |
| for (i = 0; i < count; i++) { |
| if (i == index) { |
| acpi_ut_remove_reference(*source); /* Remove one ref for being in pkg */ |
| acpi_ut_remove_reference(*source); |
| } else { |
| *dest = *source; |
| dest++; |
| } |
| |
| source++; |
| } |
| |
| /* NULL terminate list and update the package count */ |
| |
| *dest = NULL; |
| obj_desc->package.count = new_count; |
| } |