[PATCH 11/12] sbbr/method: Add initial tests to acpi method tests as per sbbr.
Supreeth Venkatesh
supreeth.venkatesh at arm.com
Thu Mar 2 22:26:35 UTC 2017
Server Base Boot Requirements (SBBR) specification is intended for SBSA-
compliant 64-bit ARMv8 servers.
It defines the base firmware requirements for out-of-box support of any
ARM SBSA-compatible Operating System or hypervisor.
The requirements in this specification are expected to be minimal yet
complete for booting a multi-core ARMv8 server platform, while leaving
plenty of room for OEM or ODM innovations and design details.
For more information, download the SBBR specification here:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html
This change introduces test cases as per SBBR specification to acpi
method tests. These test cases may be subset/superset of acpi method
tests already existing. However, to preserve "sbbr" classification, new
file is created, even when most of the code is re-used from acpi/method.
Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh at arm.com>
---
src/sbbr/method/method.c | 7333 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 7333 insertions(+)
create mode 100644 src/sbbr/method/method.c
diff --git a/src/sbbr/method/method.c b/src/sbbr/method/method.c
new file mode 100644
index 0000000..3b4dbc3
--- /dev/null
+++ b/src/sbbr/method/method.c
@@ -0,0 +1,7333 @@
+/*
+ * Copyright (C) 2010-2017 Canonical
+ * Copyright (C) 2017 ARM Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "fwts.h"
+
+#if defined(FWTS_HAS_SBBR)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include "fwts_acpi_object_eval.h"
+
+/*
+ * ACPI methods + objects used in Linux ACPI driver:
+ *
+ * Name Tested
+ * _ACx Y
+ * _ADR Y
+ * _AEI Y
+ * _ALC Y
+ * _ALI Y
+ * _ALP Y
+ * _ALR Y
+ * _ALT Y
+ * _ALx N
+ * _ART Y
+ * _BBN Y
+ * _BCL Y
+ * _BCM Y
+ * _BCT Y
+ * _BDN Y
+ * _BFS deprecated
+ * _BIF Y
+ * _BIX Y
+ * _BLT N not easily tested
+ * _BMA Y
+ * _BMC Y
+ * _BMD Y
+ * _BMS Y
+ * _BQC Y
+ * _BST Y
+ * _BTH Y
+ * _BTM Y
+ * _BTP Y
+ * _CBA Y
+ * _CCA Y
+ * _CDM Y
+ * _CID Y
+ * _CLS N requires PCI SIG class info
+ * _CPC Y
+ * _CR3 Y
+ * _CRS Y
+ * _CRT Y
+ * _CSD Y
+ * _CST Y
+ * _CWS Y
+ * _DCK Y
+ * _DCS Y
+ * _DDC Y
+ * _DDN Y
+ * _DEP Y
+ * _DGS Y
+ * _DIS Y
+ * _DLM Y
+ * _DMA Y
+ * _DOD Y
+ * _DOS Y
+ * _DSD Y
+ * _DSM N
+ * _DSS Y
+ * _DSW Y
+ * _DTI Y
+ * _Exx n/a
+ * _EC_ Y
+ * _EDL Y
+ * _EJD Y
+ * _EJx Y
+ * _EVT Y
+ * _FDE N (floppy controller, ignore)
+ * _FDI N (floppy controller, ignore)
+ * _FDM N (floppy controller, ignore)
+ * _FIF Y
+ * _FIT Y
+ * _FIX Y
+ * _FPS Y
+ * _FSL Y
+ * _FST Y
+ * _GAI Y
+ * _GCP Y
+ * _GHL Y
+ * _GL n/a
+ * _GLK Y
+ * _GPD Y
+ * _GPE Y
+ * _GRT Y
+ * _GSB Y
+ * _GTF Y
+ * _GTM Y
+ * _GTS deprecated
+ * _GWS Y
+ * _HID Y
+ * _HOT Y
+ * _HPP Y
+ * _HPX N
+ * _HRV Y
+ * _IFT Y
+ * _INI Y
+ * _IRC Y
+ * _Lxx n/a
+ * _LCK Y
+ * _LID Y
+ * _LPI Y
+ * _MAT N
+ * _MBM Y
+ * _MLS Y
+ * _MSG Y
+ * _MSM N
+ * _MTL Y
+ * _NTT Y
+ * _OFF Y
+ * _ON_ Y
+ * _OSC n/a
+ * _OST n/a
+ * _PAI n/a
+ * _PCL Y
+ * _PCT Y
+ * _PDC deprecated
+ * _PDL Y
+ * _PIC Y
+ * _PIF Y
+ * _PLD Y
+ * _PMC Y
+ * _PMD Y
+ * _PMM Y
+ * _PPC Y
+ * _PPE Y
+ * _PR0 Y
+ * _PR1 Y
+ * _PR2 Y
+ * _PR3 Y
+ * _PRE Y
+ * _PRL Y
+ * _PRR Y
+ * _PRS Y
+ * _PRT Y
+ * _PRW Y
+ * _PS0 Y
+ * _PS1 Y
+ * _PS2 Y
+ * _PS3 Y
+ * _PSC Y
+ * _PSD Y
+ * _PSE Y
+ * _PSL Y
+ * _PSR Y
+ * _PSS Y
+ * _PSV Y
+ * _PSW Y
+ * _PTC Y
+ * _PTP n/a
+ * _PTS Y
+ * _PUR Y
+ * _PXM Y
+ * _Qxx n/a
+ * _RDI Y
+ * _REG n/a
+ * _RMV Y
+ * _ROM Y
+ * _RST Y
+ * _RTV Y
+ * _S0_ Y
+ * _S1_ Y
+ * _S2_ Y
+ * _S3_ Y
+ * _S4_ Y
+ * _S5_ Y
+ * _S1D Y
+ * _S2D Y
+ * _S3D Y
+ * _S4D Y
+ * _S0W Y
+ * _S1W Y
+ * _S2W Y
+ * _S3W Y
+ * _S4W Y
+ * _SBS Y
+ * _SCP Y
+ * _SDD n/a
+ * _SEG Y
+ * _SHL n/a
+ * _SLI N
+ * _SPD Y
+ * _SRS n/a
+ * _SRT Y
+ * _SRV Y
+ * _SST Y
+ * _STA Y
+ * _STM n/a
+ * _STP Y
+ * _STR Y
+ * _STV Y
+ * _SUB Y
+ * _SUN Y
+ * _SWS Y
+ * _T_x n/a
+ * _TC1 Y
+ * _TC2 Y
+ * _TDL Y
+ * _TFP Y
+ * _TIP Y
+ * _TIV Y
+ * _TMP Y
+ * _TPC Y
+ * _TPT Y
+ * _TRT Y
+ * _TSD Y
+ * _TSN Y
+ * _TSP Y
+ * _TSS Y
+ * _TST Y
+ * _TTS Y
+ * _TZD Y
+ * _TZM Y
+ * _TZP Y
+ * _UID Y
+ * _UPC Y
+ * _UPD Y
+ * _UPP Y
+ * _VPO Y
+ * _WAK Y
+ * _WPC Y
+ * _WPP Y
+ * _Wxx n/a
+ * _WDG N
+ * _WED N
+ */
+
+/* Test types */
+#define METHOD_MANDATORY 1
+#define METHOD_OPTIONAL 2
+#define METHOD_MOBILE 4
+#define METHOD_SILENT 8
+
+#define ACPI_TYPE_INTBUF (ACPI_TYPE_INVALID + 1)
+
+#define method_check_type(fw, name, buf, type) \
+ method_check_type__(fw, name, buf, type, #type)
+
+static bool fadt_mobile_platform; /* True if a mobile platform */
+
+#define method_test_integer(name, type) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ return method_evaluate_method(fw, type, # name, \
+ NULL, 0, method_test_integer_return, # name); \
+}
+
+typedef void (*method_test_return)(fwts_framework *fw, char *name,
+ ACPI_BUFFER *ret_buff, ACPI_OBJECT *ret_obj, void *private);
+
+/*
+ * Helper functions to facilitate the evaluations
+ */
+
+/****************************************************************************/
+
+static bool method_type_matches(ACPI_OBJECT_TYPE t1, ACPI_OBJECT_TYPE t2)
+{
+ if (t1 == ACPI_TYPE_INTBUF &&
+ (t2 == ACPI_TYPE_INTEGER || t2 == ACPI_TYPE_BUFFER))
+ return true;
+
+ if (t2 == ACPI_TYPE_INTBUF &&
+ (t1 == ACPI_TYPE_INTEGER || t1 == ACPI_TYPE_BUFFER))
+ return true;
+
+ return t1 == t2;
+}
+
+/*
+ * method_passed_sane()
+ * helper function to report often used passed messages
+ */
+static void method_passed_sane(
+ fwts_framework *fw,
+ const char *name,
+ const char *type)
+{
+ fwts_passed(fw, "%s correctly returned a sane looking %s.", name, type);
+}
+
+/*
+ * method_passed_sane_uint64()
+ * helper function to report often used passed uint64 values
+ */
+static void method_passed_sane_uint64(
+ fwts_framework *fw,
+ const char *name,
+ const uint64_t value)
+{
+ fwts_passed(fw, "%s correctly returned sane looking "
+ "value 0x%8.8" PRIx64 ".", name, value);
+}
+
+/*
+ * method_failed_null_return()
+ * helper function to report often used failed NULL object return
+ */
+static void method_failed_null_object(
+ fwts_framework *fw,
+ const char *name,
+ const char *type)
+{
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "MethodReturnNullObj",
+ "%s returned a NULL object, and did not "
+ "return %s.", name, type);
+}
+
+/*
+ * method_package_count_min()
+ * check that an ACPI package has at least 'min' elements
+ */
+static int method_package_count_min(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const ACPI_OBJECT *obj,
+ const uint32_t min)
+{
+ if (obj->Package.Count < min) {
+ char tmp[128];
+
+ snprintf(tmp, sizeof(tmp), "Method%sElementCount", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s should return package of at least %" PRIu32
+ " element%s, got %" PRIu32 " element%s instead.",
+ name, min, min == 1 ? "" : "s",
+ obj->Package.Count, obj->Package.Count == 1 ? "" : "s");
+ return FWTS_ERROR;
+ }
+ return FWTS_OK;
+}
+
+/*
+ * method_package_count_equal()
+ * check that an ACPI package has exactly 'count' elements
+ */
+static int method_package_count_equal(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const ACPI_OBJECT *obj,
+ const uint32_t count)
+{
+ if (obj->Package.Count != count) {
+ char tmp[128];
+
+ snprintf(tmp, sizeof(tmp), "Method%sElementCount", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s should return package of %" PRIu32
+ " element%s, got %" PRIu32 " element%s instead.",
+ name, count, count == 1 ? "" : "s",
+ obj->Package.Count, obj->Package.Count == 1 ? "" : "s");
+ return FWTS_ERROR;
+ }
+ return FWTS_OK;
+}
+
+/*
+ * method_init()
+ * initialize ACPI
+ */
+static int sbbr_method_init(fwts_framework *fw)
+{
+ fwts_acpi_table_info *info;
+ int i;
+ bool got_fadt = false;
+
+ fadt_mobile_platform = false;
+
+ /* Some systems have multiple FADTs, sigh */
+ for (i = 0; i < 256; i++) {
+ fwts_acpi_table_fadt *fadt;
+ int ret = fwts_acpi_find_table(fw, "FACP", i, &info);
+ if (ret == FWTS_NULL_POINTER || info == NULL)
+ break;
+ fadt = (fwts_acpi_table_fadt*)info->data;
+ got_fadt = true;
+ if (fadt->preferred_pm_profile == 2) {
+ fadt_mobile_platform = true;
+ break;
+ }
+ }
+
+ if (got_fadt && !fadt_mobile_platform) {
+ fwts_log_info(fw,
+ "FADT Preferred PM profile indicates this is not "
+ "a Mobile Platform.");
+ }
+
+ if (fwts_acpi_init(fw) != FWTS_OK) {
+ fwts_log_error(fw, "Cannot initialise ACPI.");
+ return FWTS_ERROR;
+ }
+
+ return FWTS_OK;
+}
+
+/*
+ * method_deinit
+ * de-intialize ACPI
+ */
+static int sbbr_method_deinit(fwts_framework *fw)
+{
+ return fwts_acpi_deinit(fw);
+}
+
+/*
+ * method_evaluate_found_method
+ * find a given object name and evaluate it
+ */
+static void method_evaluate_found_method(
+ fwts_framework *fw,
+ char *name,
+ method_test_return check_func,
+ void *private,
+ ACPI_OBJECT_LIST *arg_list)
+{
+ ACPI_BUFFER buf;
+ ACPI_STATUS ret;
+ int sem_acquired;
+ int sem_released;
+
+ fwts_acpica_sem_count_clear();
+
+ ret = fwts_acpi_object_evaluate(fw, name, arg_list, &buf);
+
+ if (ACPI_FAILURE(ret) != AE_OK) {
+ fwts_acpi_object_evaluate_report_error(fw, name, ret);
+ } else {
+ if (check_func != NULL) {
+ ACPI_OBJECT *obj = buf.Pointer;
+ check_func(fw, name, &buf, obj, private);
+ }
+ }
+ free(buf.Pointer);
+
+ fwts_acpica_sem_count_get(&sem_acquired, &sem_released);
+ if (sem_acquired != sem_released) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "AMLLocksAcquired",
+ "%s left %d locks in an acquired state.",
+ name, sem_acquired - sem_released);
+ fwts_advice(fw,
+ "Locks left in an acquired state generally indicates "
+ "that the AML code is not releasing a lock. This can "
+ "sometimes occur when a method hits an error "
+ "condition and exits prematurely without releasing an "
+ "acquired lock. It may be occurring in the method "
+ "being tested or other methods used while evaluating "
+ "the method.");
+ }
+}
+
+/*
+ * method_evaluate_method
+ * find all matching object names and evaluate them,
+ * also run the callback check_func to sanity check
+ * any returned values
+ */
+static int method_evaluate_method(fwts_framework *fw,
+ int test_type, /* Manditory or optional */
+ char *name,
+ ACPI_OBJECT *args,
+ int num_args,
+ method_test_return check_func,
+ void *private)
+{
+ fwts_list *methods;
+ size_t name_len = strlen(name);
+ bool found = false;
+
+
+ if ((methods = fwts_acpi_object_get_names()) != NULL) {
+ fwts_list_link *item;
+
+ fwts_list_foreach(item, methods) {
+ char *method_name = fwts_list_data(char*, item);
+ ACPI_HANDLE method_handle;
+ ACPI_OBJECT_TYPE type;
+ ACPI_STATUS status;
+
+ size_t len = strlen(method_name);
+ if (strncmp(name, method_name + len - name_len, name_len) == 0) {
+ ACPI_OBJECT_LIST arg_list;
+
+ status = AcpiGetHandle (NULL, method_name, &method_handle);
+ if (ACPI_FAILURE(status)) {
+ fwts_warning(fw, "Failed to get handle for object %s.", name);
+ }
+ status = AcpiGetType(method_handle, &type);
+ if (ACPI_FAILURE(status)) {
+ fwts_warning(fw, "Failed to get object type for %s.",name);
+ }
+
+ if (type == ACPI_TYPE_LOCAL_SCOPE)
+ continue;
+
+ found = true;
+ arg_list.Count = num_args;
+ arg_list.Pointer = args;
+ method_evaluate_found_method(fw, method_name,
+ check_func, private, &arg_list);
+ }
+ }
+ }
+
+ if (found) {
+ if ((test_type & METHOD_MOBILE) && (!fadt_mobile_platform)) {
+ fwts_warning(fw,
+ "The FADT indictates that this machine is not "
+ "a mobile platform, however it has a mobile "
+ "platform specific object %s defined. "
+ "Either the FADT referred PM profile is "
+ "incorrect or this machine has mobile "
+ "platform objects defined when it should not.",
+ name);
+ }
+ return FWTS_OK;
+ } else {
+ if (!(test_type & METHOD_SILENT)) {
+ /* Mandatory not-found test are a failure */
+ if (test_type & METHOD_MANDATORY) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "MethodNotExist",
+ "Object %s did not exist.", name);
+ }
+
+ /* Mobile specific tests on non-mobile platform? */
+ if ((test_type & METHOD_MOBILE) && (!fadt_mobile_platform)) {
+ fwts_skipped(fw,
+ "Machine is not a mobile platform, skipping "
+ "test for non-existent mobile platform "
+ "related object %s.", name);
+ } else {
+ fwts_skipped(fw,
+ "Skipping test for non-existent object %s.",
+ name);
+ }
+ }
+ return FWTS_NOT_EXIST;
+
+ }
+}
+
+/*
+ * method_name_check
+ * sanity check object name conforms to ACPI specification
+ */
+static int method_name_check(fwts_framework *fw)
+{
+ fwts_list *methods;
+
+ if ((methods = fwts_acpi_object_get_names()) != NULL) {
+ fwts_list_link *item;
+ bool failed = false;
+
+ fwts_log_info(fw, "Found %d Objects\n", methods->len);
+
+ fwts_list_foreach(item, methods) {
+ char *ptr;
+
+ for (ptr = fwts_list_data(char *, item); *ptr; ptr++) {
+ if (!((*ptr == '\\') ||
+ (*ptr == '.') ||
+ (*ptr == '_') ||
+ (isdigit(*ptr)) ||
+ (isupper(*ptr))) ) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "MethodIllegalName",
+ "Method %s contains an illegal "
+ "character: '%c'. This should "
+ "be corrected.",
+ fwts_list_data(char *, item),
+ *ptr);
+ failed = true;
+ break;
+ }
+ }
+ }
+ if (!failed)
+ fwts_passed(fw, "Method names contain legal characters.");
+ }
+
+ return FWTS_OK;
+}
+
+/*
+ * method_check_type__
+ * check returned object type
+ */
+static int method_check_type__(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT_TYPE type,
+ char *type_name)
+{
+ ACPI_OBJECT *obj;
+
+ if ((buf == NULL) || (buf->Pointer == NULL)) {
+ method_failed_null_object(fw, name, type_name);
+ return FWTS_ERROR;
+ }
+
+ obj = buf->Pointer;
+
+ if (!method_type_matches(obj->Type, type)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "MethodReturnBadType",
+ "Method %s did not return %s.", name, type_name);
+ return FWTS_ERROR;
+ }
+ return FWTS_OK;
+}
+
+/*
+ * method_test_buffer_return
+ * check if a buffer object was returned
+ */
+static void method_test_buffer_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) == FWTS_OK)
+ fwts_passed(fw, "%s correctly returned a buffer of %" PRIu32 " elements.",
+ name, obj->Buffer.Length);
+}
+
+/*
+ * method_test_integer_return
+ * check if an integer object was returned
+ */
+static void method_test_integer_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(obj);
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK)
+ fwts_passed(fw, "%s correctly returned an integer.", name);
+}
+
+/*
+ * method_test_string_return
+ * check if an string object was returned
+ */
+static void method_test_string_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(obj);
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_STRING) == FWTS_OK)
+ fwts_passed(fw, "%s correctly returned a string.", name);
+}
+
+/*
+ * method_test_reference_return
+ * check if a reference object was returned
+ */
+static void method_test_reference_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(obj);
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_LOCAL_REFERENCE) == FWTS_OK)
+ fwts_passed(fw, "%s correctly returned a reference.", name);
+}
+
+/*
+ * method_test_NULL_return
+ * check if no object was retuned
+ */
+static void method_test_NULL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ /*
+ * In ACPICA SLACK mode null returns can be actually
+ * forced to return ACPI integers. Blame an errata
+ * and Windows compatibility for this mess.
+ */
+ if (fw->acpica_mode & FWTS_ACPICA_MODE_SLACK) {
+ if ((buf != NULL) && (buf->Pointer != NULL)) {
+ ACPI_OBJECT *objtmp = buf->Pointer;
+ if (method_type_matches(objtmp->Type, ACPI_TYPE_INTEGER)) {
+ fwts_passed(fw, "%s returned an ACPI_TYPE_INTEGER as expected in slack mode.",
+ name);
+ return;
+ }
+ }
+ }
+
+ if (buf && buf->Length && buf->Pointer) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "MethodShouldReturnNothing", "%s returned values, but was expected to return nothing.", name);
+ fwts_log_info(fw, "Object returned:");
+ fwts_acpi_object_dump(fw, obj);
+ fwts_advice(fw,
+ "This probably won't cause any errors, but it should "
+ "be fixed as the AML code is not conforming to the "
+ "expected behaviour as described in the ACPI "
+ "specification.");
+ } else
+ fwts_passed(fw, "%s returned no values as expected.", name);
+}
+
+/*
+ * method_test_passed_failed_return
+ * check if 0 or 1 (false/true) integer is returned
+ */
+static void method_test_passed_failed_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ char *method = (char *)private;
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK) {
+ uint32_t val = (uint32_t)obj->Integer.Value;
+ if ((val == 0) || (val == 1))
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+ else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodReturnZeroOrOne",
+ "%s returned 0x%8.8" PRIx32 ", should return 1 "
+ "(success) or 0 (failed).", method, val);
+ fwts_advice(fw,
+ "Method %s should be returning the correct "
+ "1/0 success/failed return values. "
+ "Unexpected behaviour may occur becauses of "
+ "this error, the AML code does not conform to "
+ "the ACPI specification and should be fixed.",
+ method);
+ }
+ }
+}
+
+/*
+ * method_test_polling_return
+ * check if a returned polling time is valid
+ */
+static void method_test_polling_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK) {
+ char *method = (char *)private;
+ if (obj->Integer.Value < 36000) {
+ fwts_passed(fw,
+ "%s correctly returned sane looking value "
+ "%f seconds", method,
+ (float)obj->Integer.Value / 10.0);
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodPollTimeTooLong",
+ "%s returned a value %f seconds > (1 hour) "
+ "which is probably incorrect.",
+ method, (float)obj->Integer.Value / 10.0);
+ fwts_advice(fw,
+ "The method is returning a polling interval "
+ "which is very long and hence most probably "
+ "incorrect.");
+ }
+ }
+}
+
+
+/*
+ * Common types that can be returned. This is not a complete
+ * list but it does cover the types we expect to return from
+ * an ACPI evaluation.
+ */
+static const char *method_type_name(const ACPI_OBJECT_TYPE type)
+{
+ switch (type) {
+ case ACPI_TYPE_INTEGER:
+ return "integer";
+ case ACPI_TYPE_STRING:
+ return "string";
+ case ACPI_TYPE_BUFFER:
+ return "buffer";
+ case ACPI_TYPE_PACKAGE:
+ return "package";
+ case ACPI_TYPE_BUFFER_FIELD:
+ return "buffer_field";
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ return "reference";
+ case ACPI_TYPE_INTBUF:
+ return "integer or buffer";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * method_package_elements_all_type()
+ * sanity check fields in a package that all have
+ * the same type
+ */
+static int method_package_elements_all_type(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const ACPI_OBJECT *obj,
+ const ACPI_OBJECT_TYPE type)
+{
+ uint32_t i;
+ bool failed = false;
+ char tmp[128];
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (!method_type_matches(obj->Package.Elements[i].Type, type)) {
+ snprintf(tmp, sizeof(tmp), "Method%sElementType", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s package element %" PRIu32 " was not the expected "
+ "type '%s', was instead type '%s'.",
+ name, i,
+ method_type_name(type),
+ method_type_name(obj->Package.Elements[i].Type));
+ failed = true;
+ }
+ }
+
+ return failed ? FWTS_ERROR: FWTS_OK;
+}
+
+typedef struct {
+ ACPI_OBJECT_TYPE type; /* Type */
+ const char *name; /* Field name */
+} fwts_package_element;
+
+/*
+ * method_package_elements_type()
+ * sanity check fields in a package that all have
+ * the same type
+ */
+static int method_package_elements_type(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const ACPI_OBJECT *obj,
+ const fwts_package_element *info,
+ const uint32_t count)
+{
+ uint32_t i;
+ bool failed = false;
+ char tmp[128];
+
+ if (obj->Package.Count != count)
+ return FWTS_ERROR;
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (!method_type_matches(obj->Package.Elements[i].Type, info[i].type)) {
+ snprintf(tmp, sizeof(tmp), "Method%sElementType", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s package element %" PRIu32 " (%s) was not the expected "
+ "type '%s', was instead type '%s'.",
+ name, i, info[i].name,
+ method_type_name(info[i].type),
+ method_type_name(obj->Package.Elements[i].Type));
+ failed = true;
+ }
+ }
+
+ return failed ? FWTS_ERROR: FWTS_OK;
+}
+
+/****************************************************************************/
+
+/*
+ * Section 5.6 ACPI Event Programming Model
+ */
+static void method_test_AEI_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ ACPI_STATUS status;
+ ACPI_RESOURCE *resource;
+ ACPI_RESOURCE_GPIO* gpio;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ status = AcpiBufferToResource(obj->Buffer.Pointer, obj->Buffer.Length, &resource);
+ if (ACPI_FAILURE(status))
+ return;
+
+ do {
+ if (resource->Type == ACPI_RESOURCE_TYPE_GPIO) {
+ gpio = &resource->Data.Gpio;
+ if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_AEIBadGpioElement",
+ "%s should contain only GPIO Connection Type 0, got %" PRIu32,
+ name, gpio->ConnectionType);
+ }
+ } else {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_AEIBadElement",
+ "%s should contain only Resource Type 17, got%" PRIu32,
+ name, resource->Type);
+ }
+
+ resource = ACPI_NEXT_RESOURCE(resource);
+ } while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG);
+
+ if (!failed)
+ method_passed_sane(fw, name, "buffer");
+}
+
+static int method_test_AEI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_AEI", NULL, 0, method_test_AEI_return, NULL);
+}
+
+static void check_evt_event (
+ fwts_framework *fw,
+ ACPI_RESOURCE_GPIO *gpio)
+{
+ ACPI_OBJECT arg[1];
+ ACPI_HANDLE evt_handle;
+ ACPI_STATUS status;
+ char path[256];
+ uint16_t i;
+
+ /* Skip the leading spaces in ResourceSource. */
+ for (i = 0; i < gpio->ResourceSource.StringLength; i++) {
+ if (gpio->ResourceSource.StringPtr[i] != ' ')
+ break;
+ }
+
+ if (i == gpio->ResourceSource.StringLength) {
+ fwts_log_warning(fw, "Invalid ResourceSource");
+ return;
+ }
+
+ /* Get the handle of return;the _EVT method. */
+ snprintf (path, 251, "%s._EVT", &gpio->ResourceSource.StringPtr[i]);
+
+ status = AcpiGetHandle (NULL, path, &evt_handle);
+ if (ACPI_FAILURE(status)) {
+ fwts_log_warning(fw, "Failed to find valid handle for _EVT method (0x%x), %s", status, path);
+ return;
+ }
+
+ /* Call the _EVT method with all the pins defined for the GpioInt */
+ for (i = 0; i < gpio->PinTableLength; i++) {
+ ACPI_OBJECT_LIST arg_list;
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = gpio->PinTable[i];
+ arg_list.Count = 1;
+ arg_list.Pointer = arg;
+
+ method_evaluate_found_method(fw, path, method_test_NULL_return, NULL, &arg_list);
+ }
+}
+
+static void method_test_EVT_return (
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ ACPI_RESOURCE *resource;
+ ACPI_STATUS status;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ status = AcpiBufferToResource(obj->Buffer.Pointer, obj->Buffer.Length, &resource);
+ if (ACPI_FAILURE(status))
+ return;
+
+ do {
+ if (!resource->Length) {
+ fwts_log_warning(fw, "Invalid zero length descriptor in resource list\n");
+ break;
+ }
+
+ if (resource->Type == ACPI_RESOURCE_TYPE_GPIO &&
+ resource->Data.Gpio.ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT)
+ check_evt_event(fw, &resource->Data.Gpio);
+
+ resource = ACPI_NEXT_RESOURCE(resource);
+ } while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG);
+}
+
+static int method_test_EVT(fwts_framework *fw)
+{
+ int ret;
+
+ /* Only test the _EVT method with pins defined in AEI. */
+ ret = method_evaluate_method(fw, METHOD_OPTIONAL | METHOD_SILENT,
+ "_AEI", NULL, 0, method_test_EVT_return, NULL);
+
+ if (ret == FWTS_NOT_EXIST)
+ fwts_skipped(fw, "Skipping test for non-existant object _EVT.");
+
+ return ret;
+}
+
+/*
+ * Section 5.7 Predefined Objects
+ */
+
+static void method_test_DLM_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_DLM", obj, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg = &obj->Package.Elements[i];
+
+ if (pkg->Package.Count != 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_DLMSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 2 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ if (pkg->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_DLMBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 0 is not a reference.",
+ name, i);
+ failed = true;
+ }
+
+ if (pkg->Package.Elements[1].Type != ACPI_TYPE_LOCAL_REFERENCE &&
+ pkg->Package.Elements[1].Type != ACPI_TYPE_BUFFER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_DLMBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 1 is not a reference or a buffer.",
+ name, i);
+ failed = true;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_DLM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DLM", NULL, 0, method_test_DLM_return, NULL);
+}
+
+/*
+ * Section 5.8 System Configuration Objects
+ */
+static int method_test_PIC(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ int i, ret;
+ arg[0].Type = ACPI_TYPE_INTEGER;
+
+ for (i = 0; i < 3; i++) {
+ arg[0].Integer.Value = i;
+ ret = method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PIC", arg, 1, method_test_NULL_return, NULL);
+
+ if (ret != FWTS_OK)
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * Section 6.1 Device Identification Objects
+ */
+static int method_test_DDN(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DDN", NULL, 0, method_test_string_return, NULL);
+}
+
+static bool method_valid_HID_string(char *str)
+{
+ if (strlen(str) == 7) {
+ /* PNP ID, must be 3 capitals followed by 4 hex */
+ if (!isupper(str[0]) ||
+ !isupper(str[1]) ||
+ !isupper(str[2])) return false;
+ if (!isxdigit(str[3]) ||
+ !isxdigit(str[4]) ||
+ !isxdigit(str[5]) ||
+ !isxdigit(str[6])) return false;
+ return true;
+ }
+
+ if (strlen(str) == 8) {
+ /* ACPI ID, must be 4 capitals or digits followed by 4 hex */
+ if ((!isupper(str[0]) && !isdigit(str[0])) ||
+ (!isupper(str[1]) && !isdigit(str[1])) ||
+ (!isupper(str[2]) && !isdigit(str[2])) ||
+ (!isupper(str[3]) && !isdigit(str[3]))) return false;
+ if (!isxdigit(str[4]) ||
+ !isxdigit(str[5]) ||
+ !isxdigit(str[6]) ||
+ !isxdigit(str[7])) return false;
+ return true;
+ }
+
+ return false;
+}
+
+static bool method_valid_EISA_ID(uint32_t id, char *buf, size_t buf_len)
+{
+ snprintf(buf, buf_len, "%c%c%c%02" PRIX32 "%02" PRIX32,
+ 0x40 + ((id >> 2) & 0x1f),
+ 0x40 + ((id & 0x3) << 3) + ((id >> 13) & 0x7),
+ 0x40 + ((id >> 8) & 0x1f),
+ (id >> 16) & 0xff, (id >> 24) & 0xff);
+
+ /* 3 chars in EISA ID must be upper case */
+ if (!isupper(buf[0]) ||
+ !isupper(buf[1]) ||
+ !isupper(buf[2])) return false;
+
+ /* Last 4 digits are always going to be hex, so pass */
+ return true;
+}
+
+static void method_test_HID_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ char tmp[8];
+
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(private);
+
+ if (obj == NULL) {
+ method_failed_null_object(fw, name, "a buffer or integer");
+ return;
+ }
+
+ switch (obj->Type) {
+ case ACPI_TYPE_STRING:
+ if (obj->String.Pointer) {
+ if (method_valid_HID_string(obj->String.Pointer))
+ fwts_passed(fw,
+ "%s returned a string '%s' "
+ "as expected.",
+ name, obj->String.Pointer);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodHIDInvalidString",
+ "%s returned a string '%s' "
+ "but it was not a valid PNP ID or a "
+ "valid ACPI ID.",
+ name, obj->String.Pointer);
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_HIDNullString",
+ "%s returned a NULL string.", name);
+ }
+ break;
+ case ACPI_TYPE_INTEGER:
+ if (method_valid_EISA_ID((uint32_t)obj->Integer.Value,
+ tmp, sizeof(tmp)))
+ fwts_passed(fw, "%s returned an integer "
+ "0x%8.8" PRIx64 " (EISA ID %s).",
+ name, (uint64_t)obj->Integer.Value, tmp);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodHIDInvalidInteger",
+ "%s returned a integer 0x%8.8" PRIx64 " "
+ "(EISA ID %s) but the this is not a valid "
+ "EISA ID encoded PNP ID.",
+ name, (uint64_t)obj->Integer.Value, tmp);
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_HIDBadReturnType",
+ "%s did not return a string or an integer.", name);
+ break;
+ }
+}
+
+static int method_test_HID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_HID", NULL, 0, method_test_HID_return, NULL);
+}
+
+static void method_valid_CID_Type(
+ fwts_framework *fw,
+ char *name,
+ ACPI_OBJECT *obj)
+{
+ char tmp[8];
+
+ switch (obj->Type) {
+ case ACPI_TYPE_STRING:
+ if (obj->String.Pointer) {
+ if (method_valid_HID_string(obj->String.Pointer))
+ fwts_passed(fw,
+ "%s returned a string '%s' "
+ "as expected.",
+ name, obj->String.Pointer);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodCIDInvalidString",
+ "%s returned a string '%s' "
+ "but it was not a valid PNP ID or a "
+ "valid ACPI ID.",
+ name, obj->String.Pointer);
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CIDNullString",
+ "%s returned a NULL string.", name);
+ }
+ break;
+ case ACPI_TYPE_INTEGER:
+ if (method_valid_EISA_ID((uint32_t)obj->Integer.Value,
+ tmp, sizeof(tmp)))
+ fwts_passed(fw, "%s returned an integer "
+ "0x%8.8" PRIx64 " (EISA ID %s).",
+ name, (uint64_t)obj->Integer.Value, tmp);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodCIDInvalidInteger",
+ "%s returned a integer 0x%8.8" PRIx64 " "
+ "(EISA ID %s) but the this is not a valid "
+ "EISA ID encoded PNP ID.",
+ name, (uint64_t)obj->Integer.Value, tmp);
+ break;
+ }
+}
+
+static void method_test_CID_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(private);
+
+ if (obj == NULL) {
+ method_failed_null_object(fw, name, "a buffer or integer");
+ return;
+ }
+
+ switch (obj->Type) {
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_INTEGER:
+ method_valid_CID_Type(fw, name, obj);
+ break;
+ case ACPI_TYPE_PACKAGE:
+ if (method_package_count_min(fw, name, "_CID", obj, 1) != FWTS_OK)
+ return;
+
+ for (i = 0; i < obj->Package.Count; i++){
+ ACPI_OBJECT *pkg = &obj->Package.Elements[i];
+ method_valid_CID_Type(fw, name, pkg);
+ }
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_CIDBadReturnType",
+ "%s did not return a string or an integer.", name);
+ break;
+ }
+}
+
+static int method_test_CID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CID", NULL, 0, method_test_CID_return, NULL);
+}
+
+static void method_test_MLS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_MLS", obj, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg = &obj->Package.Elements[i];
+
+ if (pkg->Package.Count != 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_MLSSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 2 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ if (pkg->Package.Elements[0].Type != ACPI_TYPE_STRING) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_MLSBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 0 is not a string.",
+ name, i);
+ failed = true;
+ }
+
+ if (pkg->Package.Elements[1].Type != ACPI_TYPE_BUFFER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_MLSBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 1 is not a buffer.",
+ name, i);
+ failed = true;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_MLS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_MLS", NULL, 0, method_test_MLS_return, NULL);
+}
+static int method_test_HRV(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_HRV", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_STR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_STR", NULL, 0, method_test_buffer_return, NULL);
+}
+
+static void method_test_PLD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* All elements in the package must be buffers */
+ if (method_package_elements_all_type(fw, name, "_PLD", obj, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PLD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PLD", NULL, 0, method_test_PLD_return, NULL);
+}
+
+static void method_test_SUB_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(private);
+
+ if (obj == NULL) {
+ method_failed_null_object(fw, name, "a buffer or integer");
+ return;
+ }
+
+ if (obj->Type == ACPI_TYPE_STRING)
+ if (obj->String.Pointer) {
+ if (method_valid_HID_string(obj->String.Pointer))
+ fwts_passed(fw,
+ "%s returned a string '%s' "
+ "as expected.",
+ name, obj->String.Pointer);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodSUBInvalidString",
+ "%s returned a string '%s' "
+ "but it was not a valid PNP ID or a "
+ "valid ACPI ID.",
+ name, obj->String.Pointer);
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SUBNullString",
+ "%s returned a NULL string.", name);
+ }
+ else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_UIDBadReturnType",
+ "Method _SUB did not return a string or an integer.");
+ }
+}
+
+
+static int method_test_SUB(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SUB", NULL, 0, method_test_SUB_return, NULL);
+}
+
+static int method_test_SUN(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SUN", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_UID_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(private);
+
+ if (obj == NULL) {
+ method_failed_null_object(fw, name, "a buffer or integer");
+ return;
+ }
+
+ switch (obj->Type) {
+ case ACPI_TYPE_STRING:
+ if (obj->String.Pointer)
+ fwts_passed(fw,
+ "%s returned a string '%s' as expected.",
+ name, obj->String.Pointer);
+ else
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_UIDNullString",
+ "%s returned a NULL string.", name);
+ break;
+ case ACPI_TYPE_INTEGER:
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_UIDBadReturnType",
+ "Method %s did not return a string or an integer.", name);
+ break;
+ }
+}
+
+static int method_test_UID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_UID", NULL, 0, method_test_UID_return, NULL);
+}
+
+/*
+ * Section 6.2 Device Configurations Objects
+ */
+static void method_test_CRS_size(
+ fwts_framework *fw,
+ const char *name, /* full _CRS or _PRS path name */
+ const char *objname, /* name of _CRS or _PRS object */
+ const char *tag, /* error log tag */
+ const size_t crs_length, /* size of _CRS buffer */
+ const size_t hdr_length, /* size of _CRS header */
+ const size_t data_length, /* length of _CRS data w/o header */
+ const size_t min, /* minimum allowed _CRS data size */
+ const size_t max, /* maximum allowed _CRS data size */
+ bool *passed) /* pass/fail flag */
+{
+ if (crs_length < data_length + hdr_length) {
+ fwts_failed(fw, LOG_LEVEL_HIGH, tag,
+ "%s Resource size is %zd bytes long but "
+ "the size stated in the %s buffer header "
+ "is %zd and hence is longer. The resource "
+ "buffer is too short.",
+ name, crs_length, objname, data_length);
+ *passed = false;
+ return;
+ }
+
+ if ((data_length < min) || (data_length > max)) {
+ if (min != max) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tag,
+ "%s Resource data size was %zd bytes long, "
+ "expected it to be between %zd and %zd bytes",
+ name, data_length, min, max);
+ *passed = false;
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tag,
+ "%s Resource data size was %zd bytes long, "
+ "expected it to be %zd bytes",
+ name, data_length, min);
+ *passed = false;
+ }
+ }
+}
+
+static void method_test_CRS_small_size(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const uint8_t *data,
+ const size_t crs_length,
+ const size_t min,
+ const size_t max,
+ bool *passed)
+{
+ size_t data_length = data[0] & 7;
+ char tmp[128];
+
+ snprintf(tmp, sizeof(tmp), "Method%sSmallResourceSize", objname);
+
+ method_test_CRS_size(fw, name, objname, tmp,
+ crs_length, 1, data_length, min, max, passed);
+}
+
+
+/*
+ * CRS small resource checks, simple checking
+ */
+static void method_test_CRS_small_resource_items(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const uint8_t *data,
+ const size_t length,
+ bool *passed,
+ const char **tag)
+{
+ uint8_t tag_item = (data[0] >> 3) & 0xf;
+ char tmp[128];
+
+ static const char *types[] = {
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "IRQ Descriptor",
+ "DMA Descriptor",
+ "Start Dependent Functions Descriptor",
+ "End Dependent Functions Descriptor",
+ "I/O Port Descriptor",
+ "Fixed Location I/O Port Descriptor",
+ "Fixed DMA Descriptor",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Vendor Defined Descriptor",
+ "End Tag Descriptor"
+ };
+
+ switch (tag_item) {
+ case 0x4: /* 6.4.2.1 IRQ Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 2, 3, passed);
+ break;
+ case 0x5: /* 6.4.2.2 DMA Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 2, 2, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ if ((data[2] & 3) == 3) {
+ snprintf(tmp, sizeof(tmp), "Method%sDmaDescriptor", objname);
+ fwts_failed(fw, LOG_LEVEL_HIGH, tmp,
+ "%s DMA transfer type preference is 0x%" PRIx8
+ " which is reserved and invalid. See "
+ "Section 6.4.2.2 of the ACPI specification.",
+ name, data[2] & 3);
+ *passed = false;
+ }
+ break;
+ case 0x6: /* 6.4.2.3 Start Dependent Functions Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 0, 1, passed);
+ break;
+ case 0x7: /* 6.4.2.4 End Dependent Functions Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 0, 0, passed);
+ break;
+ case 0x8: /* 6.4.2.5 I/O Port Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 7, 7, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ if (data[1] & 0xfe) {
+ snprintf(tmp, sizeof(tmp), "Method%sIoPortInfoReservedNonZero", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s I/O Port Descriptor Information field "
+ "has reserved bits that are non-zero, got "
+ "0x%" PRIx8 " and expected 0 or 1 for this "
+ "field. ", name, data[1]);
+ *passed = false;
+ }
+ if (((data[1] & 1) == 0) && (data[3] > 3)) {
+ snprintf(tmp, sizeof(tmp), "Method%sIoPortInfoMinBase10BitAddr", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s I/O Port Descriptor range minimum "
+ "base address is more than 10 bits however "
+ "the Information field indicates that only "
+ "a 10 bit address is being used.", name);
+ *passed = false;
+ }
+ if (((data[1] & 1) == 0) && (data[5] > 3)) {
+ snprintf(tmp, sizeof(tmp), "Method%sIoPortInfoMaxBase10BitAddr", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s I/O Port Descriptor range maximum "
+ "base address is more than 10 bits however "
+ "the Information field indicates that only "
+ "a 10 bit address is being used.", name);
+ *passed = false;
+ }
+ break;
+ case 0x9: /* 6.4.2.6 Fixed Location I/O Port Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 3, 3, passed);
+ break;
+ case 0xa: /* 6.4.2.7 Fixed DMA Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 5, 5, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ if (data[5] > 5) {
+ snprintf(tmp, sizeof(tmp), "Method%sFixedDmaTransferWidth", objname);
+ fwts_failed(fw, LOG_LEVEL_HIGH, tmp,
+ "%s DMA transfer width is 0x%" PRIx8
+ " which is reserved and invalid. See "
+ "Section 6.4.2.7 of the ACPI specification.",
+ name, data[5]);
+ *passed = false;
+ }
+ break;
+ case 0xe: /* 6.4.2.8 Vendor-Defined Descriptor */
+ method_test_CRS_small_size(fw, name, objname, data, length, 1, 7, passed);
+ break;
+ case 0xf: /* 6.4.2.9 End Tag */
+ method_test_CRS_small_size(fw, name, objname, data, length, 1, 1, passed);
+ break;
+ default:
+ snprintf(tmp, sizeof(tmp), "Method%sUnkownSmallResourceItem", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s tag bits 6:3 is an undefined "
+ "small tag item name, value 0x%" PRIx8 ".",
+ name, tag_item);
+ fwts_advice(fw,
+ "A small resource data type tag (byte 0, "
+ "bits 6:3 of the %s buffer) contains "
+ "an undefined small tag item 'name'. "
+ "The %s buffer is therefore undefined "
+ "and can't be used. See section "
+ "'6.4.2 Small Resource Data Type' of the ACPI "
+ "specification, and also table 6-161.",
+ objname, objname);
+ *passed = false;
+ break;
+ }
+
+ *tag = types[tag_item];
+}
+
+static void method_test_CRS_large_size(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const uint8_t *data,
+ const size_t crs_length,
+ const size_t min,
+ const size_t max,
+ bool *passed)
+{
+ size_t data_length;
+ char tmp[128];
+
+ /* Small _CRS resources have a 3 byte header */
+ if (crs_length < 3) {
+ snprintf(tmp, sizeof(tmp), "Method%sBufferTooSmall", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s should return a buffer of at least three bytes in length.", name);
+ *passed = false;
+ return;
+ }
+
+ data_length = (size_t)data[1] + ((size_t)data[2] << 8);
+
+ snprintf(tmp, sizeof(tmp), "Method%sLargeResourceSize",objname);
+ method_test_CRS_size(fw, name, objname, tmp,
+ crs_length, 3, data_length, min, max, passed);
+}
+
+/*
+ * Some CRS value fetching helper functions. We handle all the
+ * addresses and lengths in 64 bits to make life easier
+ */
+static uint64_t method_CRS_val64(const uint8_t *data)
+{
+ uint64_t val =
+ ((uint64_t)data[7] << 56) | ((uint64_t)data[6] << 48) |
+ ((uint64_t)data[5] << 40) | ((uint64_t)data[4] << 32) |
+ ((uint64_t)data[3] << 24) | ((uint64_t)data[2] << 16) |
+ ((uint64_t)data[1] << 8) | (uint64_t)data[0];
+
+ return val;
+}
+
+static uint64_t method_CRS_val32(const uint8_t *data)
+{
+ uint64_t val =
+ ((uint64_t)data[3] << 24) | ((uint64_t)data[2] << 16) |
+ ((uint64_t)data[1] << 8) | (uint64_t)data[0];
+
+ return val;
+}
+
+static uint64_t method_CRS_val24(const uint8_t *data)
+{
+ /* 24 bit values assume lower 8 bits are zero */
+ uint64_t val =
+ ((uint64_t)data[1] << 16) | ((uint64_t)data[0] << 8);
+
+ return val;
+}
+
+static uint64_t method_CRS_val16(const uint8_t *data)
+{
+ uint64_t val =
+ ((uint64_t)data[1] << 8) | (uint64_t)data[0];
+
+ return val;
+}
+
+/*
+ * Sanity check addresses according to table 6-179 of ACPI spec
+ */
+static void method_test_CRS_mif_maf(
+ fwts_framework *fw,
+ const char *name, /* Full _CRS or _PRS path name */
+ const char *objname, /* _CRS or _PRS name */
+ const uint8_t flag, /* _MIF _MAF flag field */
+ const uint64_t min, /* Min address */
+ const uint64_t max, /* Max address */
+ const uint64_t len, /* Range length */
+ const uint64_t granularity, /* Address granularity */
+ const char *tag, /* failed error tag */
+ const char *type, /* Resource type */
+ bool *passed)
+{
+ char tmp[128];
+ uint8_t mif = (flag >> 2) & 1;
+ uint8_t maf = (flag >> 3) & 1;
+
+ static char *mif_maf_advice =
+ "See section '6.4.3.5 Address Space Resource Descriptors' "
+ "table 6-179 of the ACPI specification for more details "
+ "about how the _MIF, _MAF and memory range and granularity "
+ "rules apply. Typically the kernel does not care about these "
+ "being correct, so this is a minor issue.";
+
+ /* Table 6-179 Valid combination of Address Space Descriptors fields */
+ if (len == 0) {
+ if ((mif == 1) && (maf == 1)) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sMifMafBothOne", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s _MIF and _MAF flags are both "
+ "set to one which is invalid when "
+ "the length field is 0.",
+ name, type);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if ((mif == 1) && (min % (granularity + 1) != 0)) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sMinNotMultipleOfGran", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s _MIN address is not a multiple "
+ "of the granularity when _MIF is 1.",
+ name, type);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if ((maf == 1) && (max % (granularity - 1) != 0)) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sMaxNotMultipleOfGran", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s _MAX address is not a multiple "
+ "of the granularity when _MAF is 1.",
+ name, type);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ } else {
+ if ((mif == 0) && (maf == 0) &&
+ (len % (granularity + 1) != 0)) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sLenNotMultipleOfGran", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s length is not a multiple "
+ "of the granularity when _MIF "
+ "and _MIF are 0.",
+ name, type);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if (((mif == 0) && (maf == 1)) ||
+ ((mif == 1) && (maf == 0))) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sMifMafInvalid", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s _MIF and _MAF flags are either "
+ "0 and 1 or 1 and 0 which is invalid when "
+ "the length field is non-zero.",
+ name, type);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if ((mif == 1) && (maf == 1)) {
+ if (granularity != 0) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sGranularityNotZero", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s granularity 0x%" PRIx64
+ " is not zero as expected when "
+ "_MIF and _MAF are both 1.",
+ name, type, granularity);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if (min > max) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sMaxLessThanMin", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s minimum address range 0x%" PRIx64
+ " is greater than the maximum address "
+ "range 0x%" PRIx64 ".",
+ name, type, min, max);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ if (max - min + 1 != len) {
+ snprintf(tmp, sizeof(tmp), "Method%s%sLengthInvalid", objname, tag);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ tmp,
+ "%s %s length 0x%" PRIx64
+ " does not match the difference between "
+ "the minimum and maximum address ranges "
+ "0x%" PRIx64 "-0x%" PRIx64 ".",
+ name, type, len, min, max);
+ fwts_advice(fw, "%s", mif_maf_advice);
+ *passed = false;
+ }
+ }
+ }
+}
+
+/*
+ * CRS large resource checks, simple checking
+ */
+static void method_test_CRS_large_resource_items(
+ fwts_framework *fw,
+ const char *name,
+ const char *objname,
+ const uint8_t *data,
+ const uint64_t length,
+ bool *passed,
+ const char **tag)
+{
+ uint64_t min, max, len, gra;
+ uint8_t tag_item = data[0] & 0x7f;
+ char tmp[128];
+
+ static const char *types[] = {
+ "Reserved",
+ "24-bit Memory Range Descriptor",
+ "Generic Register Descriptor",
+ "Reserved",
+ "Vendor Defined Descriptor",
+ "32-bit Memory Range Descriptor",
+ "32-bit Fixed Location Memory Range Descriptor",
+ "DWORD Address Space Descriptor",
+ "WORD Address Space Descriptor",
+ "Extended IRQ Descriptor",
+ "QWORD Address Space Descriptor",
+ "Extended Addresss Space Descriptor",
+ "GPIO Connection Descriptor",
+ "Reserved",
+ "Generic Serial Bus Connection Descriptor",
+ "Reserved",
+ };
+
+ switch (tag_item) {
+ case 0x1: /* 6.4.3.1 24-Bit Memory Range Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 9, 9, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ min = method_CRS_val24(&data[4]);
+ max = method_CRS_val24(&data[6]);
+ len = method_CRS_val16(&data[10]);
+ if (max < min) {
+ snprintf(tmp, sizeof(tmp), "Method%s24BitMemRangeMaxLessThanMin", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s 24-Bit Memory Range Descriptor minimum "
+ "address range 0x%" PRIx64 " is greater than "
+ "the maximum address range 0x%" PRIx64 ".",
+ name, min, max);
+ *passed = false;
+ }
+ if (len > max + 1 - min) {
+ snprintf(tmp, sizeof(tmp), "Method%s24BitMemRangeLengthTooLarge", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s 24-Bit Memory Range Descriptor length "
+ "0x%" PRIx64 " is greater than size between the "
+ "the minimum and maximum address ranges "
+ "0x%" PRIx64 "-0x%" PRIx64 ".",
+ name, len, min, max);
+ *passed = false;
+ }
+ break;
+ case 0x2: /* 6.4.3.7 Generic Register Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 12, 12, passed);
+ if (!*passed)
+ break;
+ switch (data[3]) {
+ case 0x00 ... 0x04:
+ case 0x0a:
+ case 0x7f:
+ /* Valid values */
+ break;
+ default:
+ snprintf(tmp, sizeof(tmp), "Method%sGenericRegAddrSpaceIdInvalid", objname);
+ fwts_failed(fw, LOG_LEVEL_HIGH, tmp,
+ "%s Generic Register Descriptor has an invalid "
+ "Address Space ID 0x%" PRIx8 ".",
+ name, data[3]);
+ *passed = false;
+ }
+ if (data[6] > 4) {
+ snprintf(tmp, sizeof(tmp), "Method%sGenericRegAddrSizeInvalid", objname);
+ fwts_failed(fw, LOG_LEVEL_HIGH, tmp,
+ "%s Generic Register Descriptor has an invalid "
+ "Address Access Size 0x%" PRIx8 ".",
+ name, data[6]);
+ *passed = false;
+ }
+ break;
+ case 0x4: /* 6.4.3.2 Vendor-Defined Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 0, 65535, passed);
+ break;
+ case 0x5: /* 6.4.3.3 32-Bit Memory Range Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 17, 17, passed);
+ if (!*passed)
+ break;
+ min = method_CRS_val32(&data[4]);
+ max = method_CRS_val32(&data[8]);
+ len = method_CRS_val32(&data[16]);
+ if (max < min) {
+ snprintf(tmp, sizeof(tmp), "Method%s32BitMemRangeMaxLessThanMin", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s 32-Bit Memory Range Descriptor minimum "
+ "address range 0x%" PRIx64 " is greater than "
+ "the maximum address range 0x%" PRIx64 ".",
+ name, min, max);
+ *passed = false;
+ }
+ if (len > max + 1 - min) {
+ snprintf(tmp, sizeof(tmp), "Method%s32BitMemRangeLengthTooLarge", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s 32-Bit Memory Range Descriptor length "
+ "0x%" PRIx64 " is greater than size between the "
+ "the minimum and maximum address ranges "
+ "0x%" PRIx64 "-0x%" PRIx64 ".",
+ name, len, min, max);
+ *passed = false;
+ }
+ break;
+ case 0x6: /* 6.4.3.4 32-Bit Fixed Memory Range Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 9, 9, passed);
+ /* Not much can be checked for this descriptor */
+ break;
+ case 0x7: /* 6.4.3.5.2 DWord Address Space Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 23, 65535, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ gra = method_CRS_val32(&data[6]);
+ min = method_CRS_val32(&data[10]);
+ max = method_CRS_val32(&data[14]);
+ len = method_CRS_val32(&data[22]);
+
+ method_test_CRS_mif_maf(fw, name, objname, data[4],
+ min, max, len, gra,
+ "64BitDWordAddrSpace",
+ types[0x7], passed);
+ break;
+ case 0x8: /* 6.4.3.5.3 Word Address Space Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 13, 65535, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ gra = method_CRS_val16(&data[6]);
+ min = method_CRS_val16(&data[8]);
+ max = method_CRS_val16(&data[10]);
+ len = method_CRS_val16(&data[14]);
+
+ method_test_CRS_mif_maf(fw, name, objname, data[4],
+ min, max, len, gra,
+ "64BitWordAddrSpace",
+ types[0x8], passed);
+ break;
+ case 0x9: /* 6.4.3.6 Extended Interrupt Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 6, 65535, passed);
+ /* Not much can be checked for this descriptor */
+ break;
+ case 0xa: /* 6.4.3.5.1 QWord Address Space Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 43, 65535, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ gra = method_CRS_val64(&data[6]);
+ min = method_CRS_val64(&data[14]);
+ max = method_CRS_val64(&data[22]);
+ len = method_CRS_val64(&data[38]);
+
+ method_test_CRS_mif_maf(fw, name, objname, data[4],
+ min, max, len, gra,
+ "64BitQWordAddrSpace",
+ types[0xa], passed);
+ break;
+ case 0xb: /* 6.4.3.5.4 Extended Address Space Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 53, 53, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ gra = method_CRS_val64(&data[8]);
+ min = method_CRS_val64(&data[16]);
+ max = method_CRS_val64(&data[24]);
+ len = method_CRS_val64(&data[40]);
+
+ method_test_CRS_mif_maf(fw, name, objname, data[4],
+ min, max, len, gra,
+ "64BitExtAddrSpace",
+ types[0xb], passed);
+ break;
+ case 0xc: /* 6.4.3.8.1 GPIO Connection Descriptor */
+ method_test_CRS_large_size(fw, name, objname, data, length, 22, 65535, passed);
+ if (!*passed) /* Too short, abort */
+ break;
+ if (data[4] > 2) {
+ snprintf(tmp, sizeof(tmp), "Method%sGpioConnTypeInvalid", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s GPIO Connection Descriptor has an invalid "
+ "Connection Type 0x%" PRIx8 ".",
+ name, data[2]);
+ *passed = false;
+ fwts_advice(fw,
+ "The GPIO pin connection type is "
+ "not recognised. It should be either "
+ "0x00 (interrupt connection) or "
+ "0x01 (I/O connection). See table "
+ "6-189 in section 6.4.3.8.1 of the ACPI "
+ "specification.");
+ }
+ if ((data[9] > 0x03) && (data[9] < 0x80)) {
+ snprintf(tmp, sizeof(tmp), "Method%sGpioConnTypeInvalid", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s GPIO Connection Descriptor has an invalid "
+ "Pin Configuration Type 0x%" PRIx8 ".",
+ name, data[9]);
+ *passed = false;
+ fwts_advice(fw,
+ "The GPIO pin configuration type "
+ "is not recognised. It should be one of:"
+ "0x00 (default), 0x01 (pull-up), "
+ "0x02 (pull-down), 0x03 (no-pull), "
+ "0x80-0xff (vendor defined). See table "
+ "6-189 in section 6.4.3.8.1 of the ACPI "
+ "specification.");
+ }
+ break;
+ case 0xe: /* 6.4.3.8.2 Serial Bus Connection Descriptors */
+ method_test_CRS_large_size(fw, name, objname, data, length, 11, 65535, passed);
+ /* Don't care */
+ break;
+ default:
+ snprintf(tmp, sizeof(tmp), "Method%sUnkownLargeResourceItem", objname);
+ fwts_failed(fw, LOG_LEVEL_LOW, tmp,
+ "%s tag bits 6:0 is an undefined "
+ "large tag item name, value 0x%" PRIx8 ".",
+ name, tag_item);
+ fwts_advice(fw,
+ "A large resource data type tag (byte 0 of the "
+ "%s buffer) contains an undefined large tag "
+ "item 'name'. The %s buffer is therefore "
+ "undefined and can't be used. See section "
+ "'6.4.3 Large Resource Data Type' of the ACPI "
+ "specification, and also table 6-173.",
+ objname, objname);
+ *passed = false;
+ break;
+ }
+
+ *tag = types[tag_item < 16 ? tag_item : 0];
+}
+
+static void method_test_CRS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint8_t *data;
+ bool passed = true;
+ const char *tag = "Unknown";
+ char *objname = (char*)private;
+ char tmp[128];
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+ if (obj->Buffer.Pointer == NULL) {
+ snprintf(tmp, sizeof(tmp), "Method%sNullBuffer", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s returned a NULL buffer pointer.", name);
+ return;
+ }
+ if (obj->Buffer.Length < 1) {
+ snprintf(tmp, sizeof(tmp), "Method%sBufferTooSmall", objname);
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, tmp,
+ "%s should return a buffer of at least one byte in length.", name);
+ return;
+ }
+
+ data = (uint8_t*)obj->Buffer.Pointer;
+
+ if (data[0] & 128)
+ method_test_CRS_large_resource_items(fw, name, objname, data, obj->Buffer.Length, &passed, &tag);
+ else
+ method_test_CRS_small_resource_items(fw, name, objname, data, obj->Buffer.Length, &passed, &tag);
+
+ if (passed)
+ fwts_passed(fw, "%s (%s) looks sane.", name, tag);
+}
+
+static int method_test_CRS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_CRS", NULL, 0, method_test_CRS_return, "_CRS");
+}
+
+static int method_test_PRS(fwts_framework *fw)
+{
+ /* Re-use the _CRS checking on the returned buffer */
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PRS", NULL, 0, method_test_CRS_return, "_PRS");
+}
+
+static void method_test_PRT_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i, j;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PRT", obj, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ ACPI_OBJECT *element;
+ pkg = &obj->Package.Elements[i];
+
+ /* check size of sub-packages */
+ if (pkg->Package.Count != 4) {
+ fwts_failed(fw, LOG_LEVEL_CRITICAL,
+ "Method_PRTSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to have 4"
+ "elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ /* check types of sub-packages' elements */
+ for (j = 0; j < 4; j++) {
+ element = &pkg->Package.Elements[j];
+
+ if (j == 2) {
+ if (element->Type != ACPI_TYPE_INTEGER && element->Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_CRITICAL,
+ "Method_PRTBadSubElementType",
+ "%s element %" PRIu32 " is not an integer or a NamePath.", name, j);
+ failed = true;
+ }
+ continue;
+ }
+
+ if (element->Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_CRITICAL,
+ "Method_PRTBadSubElementType",
+ "%s element %" PRIu32 " is not an integer.", name, j);
+ failed = true;
+ }
+ }
+
+ /* check sub-packages's PCI address */
+ element = &pkg->Package.Elements[0];
+ if ((element->Integer.Value & 0xFFFF) != 0xFFFF) {
+ fwts_failed(fw, LOG_LEVEL_CRITICAL,
+ "Method_PRTBadSubElement",
+ "%s element 0 is expected to end with 0xFFFF, got 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ /* check sub-packages's PCI pin number */
+ element = &pkg->Package.Elements[1];
+ if (element->Integer.Value > 3) {
+ fwts_failed(fw, LOG_LEVEL_CRITICAL,
+ "Method_PRTBadSubElement",
+ "%s element 1 is expected to be 0..3, got 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PRT(fwts_framework *fw)
+{
+ /* Re-use the _CRS checking on the returned buffer */
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PRT", NULL, 0, method_test_PRT_return, "_PRT");
+}
+
+static int method_test_DMA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DMA", NULL, 0, method_test_buffer_return, NULL);
+}
+
+static void method_test_FIX_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ char tmp[8];
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* All elements in the package must be integers */
+ if (method_package_elements_all_type(fw, name, "_FIX", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ /* And they need to be valid IDs */
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (!method_valid_EISA_ID(
+ (uint32_t)obj->Package.Elements[i].Integer.Value,
+ tmp, sizeof(tmp))) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FIXInvalidElementValue",
+ "%s returned an integer "
+ "0x%8.8" PRIx64 " in package element "
+ "%" PRIu32 " that is not a valid "
+ "EISA ID.", name,
+ (uint64_t)obj->Integer.Value, i);
+ failed = true;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_FIX(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FIX", NULL, 0, method_test_FIX_return, NULL);
+}
+
+/*
+ * Section 6.2.5 _DSD Device Specific Data, ACPI 5.1
+ */
+static void method_test_DSD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Must be an even number of items in package */
+ if (obj->Package.Count & 1) {
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_DSDElementCount",
+ "There must be an even number of items in the %s "
+ "package, instead, got %" PRIu32 " elements.",
+ name, obj->Package.Count);
+ return;
+ }
+ for (i = 0; i < obj->Package.Count; i += 2) {
+ /* UUID should be a buffer */
+ if (!method_type_matches(obj->Package.Elements[i].Type, ACPI_TYPE_BUFFER)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_DSDElementBuffer",
+ "%s package element %" PRIu32 " was not the expected "
+ "type '%s', was instead type '%s'.",
+ name, i,
+ method_type_name(ACPI_TYPE_BUFFER),
+ method_type_name(obj->Package.Elements[i].Type));
+ }
+
+ /* Data should be a package */
+ if (!method_type_matches(obj->Package.Elements[i + 1].Type, ACPI_TYPE_PACKAGE)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_DSDElementPackage",
+ "%s package element %" PRIu32 " was not the expected "
+ "type '%s', was instead type '%s'.",
+ name, i + 1,
+ method_type_name(ACPI_TYPE_PACKAGE),
+ method_type_name(obj->Package.Elements[i + 1].Type));
+ }
+ }
+}
+
+static int method_test_DSD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DSD", NULL, 0, method_test_DSD_return, NULL);
+}
+
+static int method_test_DIS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DIS", NULL, 0, method_test_NULL_return, NULL);
+}
+
+static int method_test_GSB(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GSB", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_HPP_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Must be 4 elements in the package */
+ if (method_package_count_equal(fw, name, "_HPP", obj, 4) != FWTS_OK)
+ return;
+
+ /* All 4 elements in the package must be integers */
+ if (method_package_elements_all_type(fw, name, "_HPP", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_HPP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_HPP", NULL, 0, method_test_HPP_return, NULL);
+}
+
+static int method_test_PXM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PXM", NULL, 0, method_test_integer_return, NULL);
+}
+
+/* Section 6.2.17 _CCA */
+static int method_test_CCA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CCA", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 6.3 Device Insertion, Removal and Status Objects
+ */
+static void method_test_EDL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_EDL",
+ obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_EDL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_EDL", NULL, 0, method_test_EDL_return, NULL);
+}
+
+static int method_test_EJD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_EJD", NULL, 0, method_test_string_return, NULL);
+}
+
+#define method_test_EJx(name) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ ACPI_OBJECT arg[1]; \
+ \
+ arg[0].Type = ACPI_TYPE_INTEGER; \
+ arg[0].Integer.Value = 1; \
+ \
+ return method_evaluate_method(fw, METHOD_OPTIONAL, \
+ # name, arg, 1, method_test_NULL_return, # name); \
+}
+
+method_test_EJx(_EJ0)
+method_test_EJx(_EJ1)
+method_test_EJx(_EJ2)
+method_test_EJx(_EJ3)
+method_test_EJx(_EJ4)
+
+static int method_test_LCK(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_LCK", arg, 1, method_test_NULL_return, NULL);
+}
+
+static int method_test_RMV(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_RMV",
+ NULL, 0, method_test_passed_failed_return, "_RMV");
+}
+
+static void method_test_STA_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if ((obj->Integer.Value & 3) == 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_STAEnabledNotPresent",
+ "%s indicates that the device is enabled "
+ "but not present, which is impossible.", name);
+ failed = true;
+ }
+ if ((obj->Integer.Value & ~0x1f) != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_STAReservedBitsSet",
+ "%s is returning non-zero reserved "
+ "bits 5-31. These should be zero.", name);
+ failed = true;
+ }
+
+ if (!failed)
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_STA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_STA",
+ NULL, 0, method_test_STA_return, "_STA");
+}
+
+
+/*
+ * Section 6.5 Other Objects and Controls
+ */
+static int method_test_BBN(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_BBN",
+ NULL, 0, method_test_integer_return, "_BBN");
+}
+
+static int method_test_BDN(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE, "_BDN",
+ NULL, 0, method_test_integer_return, "_BDN");
+}
+
+static void method_test_DEP_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_DEP", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_DEP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DEP", NULL, 0, method_test_DEP_return, NULL);
+}
+
+static int method_test_FIT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FIT", NULL, 0, method_test_buffer_return, NULL);
+}
+
+static int method_test_DCK(fwts_framework *fw)
+{
+ int i;
+
+ for (i = 0; i <= 1; i++) { /* Undock, Dock */
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i;
+ if (method_evaluate_method(fw, METHOD_MOBILE, "_DCK", arg,
+ 1, method_test_passed_failed_return, "_DCK") != FWTS_OK)
+ break;
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+static int method_test_INI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_INI", NULL, 0, method_test_NULL_return, NULL);
+}
+
+static void method_test_SEG_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if ((obj->Integer.Value & 0xffff0000)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SEGIllegalReserved",
+ "%s returned value 0x%8.8" PRIx64 " and some of the "
+ "upper 16 reserved bits are set when they "
+ "should in fact be zero.",
+ name, (uint64_t)obj->Integer.Value);
+ } else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_SEG(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_SEG",
+ NULL, 0, method_test_SEG_return, "_SEG");
+}
+
+static void method_test_GLK_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(private);
+
+ switch (obj->Type) {
+ case ACPI_TYPE_INTEGER:
+ if (obj->Integer.Value == 0 || obj->Integer.Value == 1)
+ fwts_passed(fw, "%s returned an integer 0x%8.8" PRIx64,
+ name, (uint64_t)obj->Integer.Value);
+ else
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "MethodGLKInvalidInteger",
+ "%s returned an invalid integer 0x%8.8" PRIx64,
+ name, (uint64_t)obj->Integer.Value);
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_GLKBadReturnType",
+ "%s did not return an integer.", name);
+ break;
+ }
+}
+
+static int method_test_GLK(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_GLK",
+ NULL, 0, method_test_GLK_return, "_GLK");
+}
+
+/*
+ * Section 7.1 Declaring a Power Resource Object
+ */
+static int method_test_ON_(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ON_", NULL, 0, method_test_NULL_return, NULL);
+}
+
+static int method_test_OFF(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_OFF", NULL, 0, method_test_NULL_return, NULL);
+}
+
+
+/*
+ * Section 7.2 Device Power Management Objects
+ */
+static int method_test_DSW(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[3];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 0;
+ arg[2].Type = ACPI_TYPE_INTEGER;
+ arg[2].Integer.Value = 3;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_DSW",
+ arg, 3, method_test_NULL_return, NULL);
+}
+
+static int method_test_PSx(fwts_framework *fw, char *name)
+{
+ /*
+ * iASL (ACPICA commit 6922796cfdfca041fdb96dc9e3918cbc7f43d830)
+ * checks that _PS0 must exist if we have _PS1, _PS2, _PS3
+ * so check this here too.
+ */
+ if ((fwts_acpi_object_exists(name) != NULL) &&
+ (fwts_acpi_object_exists("_PS0") == NULL)) {
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_PSx",
+ "%s requires that the _PS0 "
+ "control method must also exist, however, "
+ "it was not found.", name);
+ }
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ name, NULL, 0, method_test_NULL_return, name);
+}
+
+static void method_test_PRW_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_min(fw, name, "_PRW", obj, 2) != FWTS_OK)
+ return;
+
+ if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER &&
+ obj->Package.Elements[0].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWBadPackageReturnType",
+ "%s element 0 is not an integer or an package.", name);
+ failed = true;
+ }
+
+ if (obj->Package.Elements[0].Type == ACPI_TYPE_PACKAGE) {
+ ACPI_OBJECT *pkg;
+ pkg = &obj->Package.Elements[0];
+ if (pkg->Package.Count != 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWSubPackageElementCount",
+ "%s sub-package 0 was expected to have 2"
+ "elements, got %" PRIu32 " elements instead.",
+ name, pkg->Package.Count);
+ failed = true;
+ }
+
+ if (pkg->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWBadSubPackageElementType",
+ "%s sub-package 0 element 0 is not "
+ "a reference.",name);
+ failed = true;
+ }
+
+ if (pkg->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWBadSubPackageElementType",
+ "%s sub-package 0 element 0 is not "
+ "an integer.",name);
+ failed = true;
+ }
+ }
+
+ if (obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWBadPackageReturnType",
+ "%s element 1 is not an integer.", name);
+ failed = true;
+ }
+
+ for (i = 2; i < obj->Package.Count - 1; i++) {
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRWBadPackageReturnType",
+ "%s package %" PRIu32
+ " element 0 is not a reference.",
+ name, i);
+ failed = true;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PRW(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PRW", NULL, 0, method_test_PRW_return, NULL);
+}
+
+static int method_test_PS0(fwts_framework *fw)
+{
+ /*
+ * iASL (ACPICA commit 6922796cfdfca041fdb96dc9e3918cbc7f43d830)
+ * checks that one of _PS1, _PS2, _PS3 must exist if _PS0 exists.
+ */
+ if (fwts_acpi_object_exists("_PS0") != NULL) {
+ bool ok = false;
+ int i;
+
+ for (i = 1; i < 4; i++) {
+ char name[5];
+
+ snprintf(name, sizeof(name), "_PS%1d", i);
+ if (fwts_acpi_object_exists(name) != NULL) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_PS0",
+ "_PS0 requires that one of the _PS1, _PS2, _PS3 "
+ "control methods must also exist, however, "
+ "none were found.");
+ }
+ }
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_PS0",
+ NULL, 0, method_test_NULL_return, "_PS0");
+}
+
+static int method_test_PS1(fwts_framework *fw)
+{
+ return method_test_PSx(fw, "_PS1");
+}
+
+static int method_test_PS2(fwts_framework *fw)
+{
+ return method_test_PSx(fw, "_PS2");
+}
+
+static int method_test_PS3(fwts_framework *fw)
+{
+ return method_test_PSx(fw, "_PS3");
+}
+
+static int method_test_PSC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSC", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_PSE(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSE", arg, 1, method_test_NULL_return, NULL);
+}
+
+static void method_test_power_resources_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ char *objname = (char *)private;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, objname,
+ obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+#define method_test_POWER(name) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ return method_evaluate_method(fw, METHOD_OPTIONAL, \
+ # name, NULL, 0, method_test_power_resources_return, # name);\
+}
+
+method_test_POWER(_PR0)
+method_test_POWER(_PR1)
+method_test_POWER(_PR2)
+method_test_POWER(_PR3)
+method_test_POWER(_PRE)
+
+static int method_test_PSW(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSW", arg, 1, method_test_NULL_return, NULL);
+}
+
+#define method_test_SxD(name) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ return method_evaluate_method(fw, METHOD_OPTIONAL, \
+ # name, NULL, 0, method_test_integer_return, # name); \
+}
+
+method_test_SxD(_S1D)
+method_test_SxD(_S2D)
+method_test_SxD(_S3D)
+method_test_SxD(_S4D)
+
+#define method_test_SxW(name) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ return method_evaluate_method(fw, METHOD_OPTIONAL, \
+ # name, NULL, 0, method_test_integer_return, # name); \
+}
+
+method_test_SxW(_S0W)
+method_test_SxW(_S1W)
+method_test_SxW(_S2W)
+method_test_SxW(_S3W)
+method_test_SxW(_S4W)
+
+static int method_test_RST(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_RST", NULL, 0, method_test_NULL_return, NULL);
+}
+
+static void method_test_PRR_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_PRR", obj, 1) != FWTS_OK)
+ return;
+
+ if (obj->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PRRElementType",
+ "%s returned a package that does not contain "
+ "a reference.", name);
+ return;
+ }
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PRR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PRR", NULL, 0, method_test_PRR_return, NULL);
+}
+
+static int method_test_IRC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_IRC", NULL, 0, method_test_NULL_return, NULL);
+}
+
+/*
+ * Section 7.3 OEM Supplied System-Level Control Methods
+ */
+static void method_test_Sx__return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /*
+ * The ACPI spec states it should have 1 integer, with the
+ * values packed into each byte. However, nearly all BIOS
+ * vendors don't do this, instead they return a package of
+ * 2 or more integers with each integer lower byte containing
+ * the data we are interested in. The kernel handles this
+ * the non-compliant way. Doh. See drivers/acpi/acpica/hwxface.c
+ * for the kernel implementation and also
+ * source/components/hardware/hwxface.c in the reference ACPICA
+ * sources.
+ */
+
+ /* Something is really wrong if we don't have any elements in _Sx_ */
+ if (obj->Package.Count < 1) {
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_SxElementCount",
+ "The kernel expects a package of at least two "
+ "integers, and %s only returned %" PRIu32
+ " elements in the package.",
+ name, obj->Package.Count);
+ return;
+ }
+
+ /*
+ * Oh dear, BIOS is conforming to the spec but won't work in
+ * Linux
+ */
+ if (obj->Package.Count == 1) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_SxElementCount",
+ "The ACPI specification states that %s should "
+ "return a package of a single integer which "
+ "this firmware does do. However, nearly all of the "
+ "BIOS vendors return the values in the low 8 bits "
+ "in a package of 2 to 4 integers which is not "
+ "compliant with the specification BUT is the way "
+ "that the ACPICA reference engine and the kernel "
+ "expect. So, while this is conforming to the ACPI "
+ "specification it will in fact not work in the "
+ "Linux kernel.", name);
+ return;
+ }
+
+ /* Yes, we really want integers! */
+ if ((obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER) ||
+ (obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SxElementType",
+ "%s returned a package that did not contain "
+ "an integer.", name);
+ return;
+ }
+
+ if (obj->Package.Elements[0].Integer.Value & 0xffffff00) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SxElementValue",
+ "%s package element 0 had upper 24 bits "
+ "of bits that were non-zero.", name);
+ failed = true;
+ }
+
+ if (obj->Package.Elements[1].Integer.Value & 0xffffff00) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SxElementValue",
+ "%s package element 1 had upper 24 bits "
+ "of bits that were non-zero.", name);
+ failed = true;
+ }
+
+ fwts_log_info(fw, "%s PM1a_CNT.SLP_TYP value: 0x%8.8" PRIx64, name,
+ (uint64_t)obj->Package.Elements[0].Integer.Value);
+ fwts_log_info(fw, "%s PM1b_CNT.SLP_TYP value: 0x%8.8" PRIx64, name,
+ (uint64_t)obj->Package.Elements[1].Integer.Value);
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+#define method_test_Sx_(name) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ return method_evaluate_method(fw, METHOD_OPTIONAL, \
+ # name, NULL, 0, method_test_Sx__return, # name); \
+}
+
+method_test_Sx_(_S0_)
+method_test_Sx_(_S1_)
+method_test_Sx_(_S2_)
+method_test_Sx_(_S3_)
+method_test_Sx_(_S4_)
+method_test_Sx_(_S5_)
+
+static int method_test_SWS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SWS", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 8.4 Declaring Processors
+ */
+static void method_test_CPC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint8_t revision;
+
+ static fwts_package_element elementsv1[] = {
+ { ACPI_TYPE_INTEGER, "Number of Entries" },
+ { ACPI_TYPE_INTEGER, "Revision" },
+ { ACPI_TYPE_INTBUF, "Highest Performance" },
+ { ACPI_TYPE_INTBUF, "Nominal Performance" },
+ { ACPI_TYPE_INTBUF, "Lowest Non Linear Performance" },
+ { ACPI_TYPE_INTBUF, "Lowest Performance" },
+ { ACPI_TYPE_BUFFER, "Guaranteed Performance Register" },
+ { ACPI_TYPE_BUFFER, "Desired Performance Register" },
+ { ACPI_TYPE_BUFFER, "Minimum Performance Register" },
+ { ACPI_TYPE_BUFFER, "Maximum Performance Register" },
+ { ACPI_TYPE_BUFFER, "Performance Reduction Tolerance Register" },
+ { ACPI_TYPE_BUFFER, "Timed Window Register" },
+ { ACPI_TYPE_INTBUF, "Counter Wraparound Time" },
+ { ACPI_TYPE_BUFFER, "Nominal Counter Register" },
+ { ACPI_TYPE_BUFFER, "Delivered Counter Register" },
+ { ACPI_TYPE_BUFFER, "Performance Limited Register" },
+ { ACPI_TYPE_BUFFER, "Enable Register" }
+ };
+
+ static fwts_package_element elementsv2[] = {
+ { ACPI_TYPE_INTEGER, "Number of Entries" },
+ { ACPI_TYPE_INTEGER, "Revision" },
+ { ACPI_TYPE_INTBUF, "Highest Performance" },
+ { ACPI_TYPE_INTBUF, "Nominal Performance" },
+ { ACPI_TYPE_INTBUF, "Lowest Non Linear Performance" },
+ { ACPI_TYPE_INTBUF, "Lowest Performance" },
+ { ACPI_TYPE_BUFFER, "Guaranteed Performance Register" },
+ { ACPI_TYPE_BUFFER, "Desired Performance Register" },
+ { ACPI_TYPE_BUFFER, "Minimum Performance Register" },
+ { ACPI_TYPE_BUFFER, "Maximum Performance Register" },
+ { ACPI_TYPE_BUFFER, "Performance Reduction Tolerance Register" },
+ { ACPI_TYPE_BUFFER, "Timed Window Register" },
+ { ACPI_TYPE_INTBUF, "Counter Wraparound Time" },
+ { ACPI_TYPE_BUFFER, "Reference Performance Counter Register" },
+ { ACPI_TYPE_BUFFER, "Delivered Performance Counter Register" },
+ { ACPI_TYPE_BUFFER, "Performance Limited Register" },
+ { ACPI_TYPE_BUFFER, "CPPC Enable Register" },
+ { ACPI_TYPE_INTBUF, "Autonomous Selection Enable" },
+ { ACPI_TYPE_BUFFER, "Autonomous Activity Window Register" },
+ { ACPI_TYPE_BUFFER, "Energy Performance Preference Register" },
+ { ACPI_TYPE_INTBUF, "Reference Performance" }
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ revision = obj->Package.Elements[1].Integer.Value;
+
+ if (revision == 1) { // acpi 5.0
+ /* Something is really wrong if we don't have any elements in _CPC */
+ if (method_package_count_equal(fw, name, "_CPC", obj, 17) != FWTS_OK)
+ return;
+
+ /* For now, just check types */
+ if (method_package_elements_type(fw, name, "_CPC", obj, elementsv1, 17) != FWTS_OK)
+ return;
+ } else if (revision == 2) { // acpi 5.1 and later
+ /* Something is really wrong if we don't have any elements in _CPC */
+ if (method_package_count_equal(fw, name, "_CPC", obj, 21) != FWTS_OK)
+ return;
+
+ /* For now, just check types */
+ if (method_package_elements_type(fw, name, "_CPC", obj, elementsv2, 21) != FWTS_OK)
+ return;
+ } else {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_CPCBadRevision",
+ "_CPC's _REV is incorrect, "
+ "expecting 1 or 2, got 0x%" PRIx8 , revision);
+
+ return;
+ }
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_CPC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_CPC", NULL,
+ 0, method_test_CPC_return, NULL);
+}
+
+static void method_test_CSD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Something is really wrong if we don't have any elements in _CSD */
+ if (method_package_count_min(fw, name, "_CSD", obj, 1) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ if (method_package_elements_all_type(fw, name, "_CSD",
+ obj, ACPI_TYPE_PACKAGE) != FWTS_OK) {
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ pkg = &obj->Package.Elements[i];
+ /*
+ * Currently we expect a package of 6 integers.
+ */
+ if (pkg->Package.Count != 6) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSDSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 5 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ for (j = 0; j < 6; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSDSubPackageElementCount",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+
+ /* Element 0 must equal the number elements in the package */
+ if (pkg->Package.Elements[0].Integer.Value != pkg->Package.Count) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSDSubPackageElement0",
+ "%s sub-package %d element 0 (NumEntries) "
+ "was expected to have value 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+ /* Element 1 should contain zero */
+ if (pkg->Package.Elements[1].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSDSubPackageElement1",
+ "%s sub-package %d element 1 (Revision) "
+ "was expected to have value 1, instead it "
+ "was 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[1].Integer.Value);
+ failed = true;
+ }
+ /* Element 3 should contain 0xfc..0xfe */
+ if ((pkg->Package.Elements[3].Integer.Value != 0xfc) &&
+ (pkg->Package.Elements[3].Integer.Value != 0xfd) &&
+ (pkg->Package.Elements[3].Integer.Value != 0xfe)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSDSubPackageElement1",
+ "%s sub-package %d element 3 (CoordType) "
+ "was expected to have value 0xfc (SW_ALL), "
+ "0xfd (SW_ANY) or 0xfe (HW_ALL), instead it "
+ "was 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[3].Integer.Value);
+ failed = true;
+ }
+ /* Element 4 number of processors, skip check */
+ /* Element 5 index, check */
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_CSD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CSD", NULL, 0, method_test_CSD_return, NULL);
+}
+
+static void method_test_CST_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i, j;
+ bool failed = false;
+ bool *cst_elements_ok;
+ bool an_element_ok = false;
+
+ typedef struct {
+ const uint32_t type;
+ const char *name;
+ } cstate_info;
+
+ static const cstate_info cstate_types[] = {
+ { ACPI_TYPE_BUFFER, "buffer" },
+ { ACPI_TYPE_INTEGER, "integer" },
+ { ACPI_TYPE_INTEGER, "integer" },
+ { ACPI_TYPE_INTEGER, "integer" },
+ };
+
+ FWTS_UNUSED(private);
+
+ if (obj == NULL)
+ return;
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* _CST has at least two elements */
+ if (method_package_count_min(fw, name, "_CST", obj, 2) != FWTS_OK)
+ return;
+
+ /* Element 1 must be an integer */
+ if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_CSTElement0NotInteger",
+ "%s should return package with element zero being an integer "
+ "count of the number of C state sub-packages.", name);
+ return;
+ }
+
+ if (obj->Package.Elements[0].Integer.Value != obj->Package.Count - 1) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_CSTElement0CountMismatch",
+ "%s should return package with element zero containing "
+ "the number of C state sub-elements. However, _CST has "
+ "%" PRIu32 " returned C state sub-elements yet _CST "
+ "reports it has %" PRIu64 " C states.",
+ name, obj->Package.Count - 1,
+ (uint64_t)obj->Package.Elements[0].Integer.Value);
+ return;
+ }
+
+ cst_elements_ok = calloc(obj->Package.Count, sizeof(bool));
+ if (cst_elements_ok == NULL) {
+ fwts_log_error(fw, "Cannot allocate an array. Test aborted.");
+ return;
+ }
+
+ /* Could be one or more packages */
+ for (i = 1; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+
+ cst_elements_ok[i] = true;
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSTElementType",
+ "%s package element %" PRIu32 " was not a package.",
+ name, i);
+ cst_elements_ok[i] = false;
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ pkg = &obj->Package.Elements[i];
+
+ if (pkg->Package.Count != 4) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSTElementPackageCountInvalid",
+ "%s package element %" PRIu32 " should have "
+ "4 elements, instead it had %" PRIu32 ".",
+ name, i, pkg->Package.Count);
+ cst_elements_ok[i] = false;
+ failed = true;
+ continue;
+ }
+
+ for (j = 0; j < 4; j++) {
+ if (pkg->Package.Elements[j].Type != cstate_types[j].type) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSTCStatePackageElementInvalidType",
+ "%s C-State package %" PRIu32 " element %" PRIu32
+ " was not a %s.",
+ name, i, j, cstate_types[j].name);
+ cst_elements_ok[i] = false;
+ failed = true;
+ }
+ }
+
+ /* Some very simple sanity checks on Register Resource Buffer */
+ if (pkg->Package.Elements[0].Type == ACPI_TYPE_BUFFER) {
+ if (pkg->Package.Elements[0].Buffer.Pointer == NULL) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSTCStateRegisterResourceBufferNull",
+ "%s C-State package %" PRIu32 " has a NULL "
+ "Register Resource Buffer", name, i);
+ failed = true;
+ } else {
+ uint8_t *data = (uint8_t *)pkg->Package.Elements[0].Buffer.Pointer;
+ size_t length = (size_t)pkg->Package.Elements[0].Buffer.Length;
+
+ if (data[0] != 0x82) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CSTCStateResourceBufferWrongType",
+ "%s C-State package %" PRIu32 " has a Resource "
+ "type 0x%2.2" PRIx8 ", however, was expecting a Register "
+ "Resource type 0x82.", name, i, data[0]);
+ failed = true;
+ }
+ else {
+ bool passed = true;
+ method_test_CRS_large_size(fw, name, "_CST", data, length, 12, 12, &passed);
+ if (!passed)
+ failed = true;
+ }
+ }
+ }
+
+ if (cst_elements_ok[i])
+ an_element_ok = true;
+ }
+
+ /* Now dump out per CPU C-state information */
+ if (an_element_ok) {
+ fwts_log_info_verbatim(fw, "%s values:", name);
+ fwts_log_info_verbatim(fw, "# C-State Latency Power");
+ fwts_log_info_verbatim(fw, " (us) (mW)");
+ for (i = 1; i < obj->Package.Count; i++){
+ if (cst_elements_ok[i]) {
+ ACPI_OBJECT *pkg = &obj->Package.Elements[i];
+ fwts_log_info_verbatim(fw,
+ "%2" PRIu32 " C%" PRIu64 " %6" PRIu64 " %6" PRIu64,
+ i,
+ (uint64_t)pkg->Package.Elements[1].Integer.Value,
+ (uint64_t)pkg->Package.Elements[2].Integer.Value,
+ (uint64_t)pkg->Package.Elements[3].Integer.Value);
+ } else {
+ fwts_log_info_verbatim(fw,
+ "%2" PRIu32 " -- ----- -----", i);
+ }
+ }
+ }
+
+ free(cst_elements_ok);
+
+ if (!failed)
+ method_passed_sane(fw, name, "values");
+}
+
+static int method_test_CST(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CST", NULL, 0, method_test_CST_return, NULL);
+}
+
+static void method_test_PCT_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Something is really wrong if we don't have any elements in _PCT */
+ if (method_package_count_min(fw, name, "_PCT", obj, 2) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PCT",
+ obj, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PCT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_PCT", NULL,
+ 0, method_test_PCT_return, NULL);
+}
+
+static void method_test_PSS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+ uint32_t max_freq = 0;
+ uint32_t prev_power = 0;
+ bool max_freq_valid = false;
+ bool dump_elements = false;
+ bool *element_ok;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Something is really wrong if we don't have any elements in _PSS */
+ if (method_package_count_min(fw, name, "_PSS", obj, 1) != FWTS_OK)
+ return;
+
+ element_ok = calloc(obj->Package.Count, sizeof(bool));
+ if (element_ok == NULL) {
+ fwts_log_error(fw, "Cannot allocate an array. Test aborted.");
+ return;
+ }
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pstate;
+
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSSElementType",
+ "%s package element %" PRIu32
+ " was not a package.", name, i);
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ pstate = &obj->Package.Elements[i];
+ if (pstate->Package.Count != 6) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSSSubPackageElementCount",
+ "%s P-State sub-package %" PRIu32
+ " was expected to "
+ "have 6 elements, got %" PRIu32 " elements instead.",
+ name, i, obj->Package.Count);
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ /* Elements need to be all ACPI integer types */
+ if ((pstate->Package.Elements[0].Type != ACPI_TYPE_INTEGER) ||
+ (pstate->Package.Elements[1].Type != ACPI_TYPE_INTEGER) ||
+ (pstate->Package.Elements[2].Type != ACPI_TYPE_INTEGER) ||
+ (pstate->Package.Elements[3].Type != ACPI_TYPE_INTEGER) ||
+ (pstate->Package.Elements[4].Type != ACPI_TYPE_INTEGER) ||
+ (pstate->Package.Elements[5].Type != ACPI_TYPE_INTEGER)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSSSubPackageElementType",
+ "%s P-State sub-package %" PRIu32 " was expected to "
+ "have 6 Integer elements but didn't", name, i);
+ failed = true;
+ continue;
+ }
+
+ /*
+ * Parses OK, so this element can be dumped out
+ */
+ element_ok[i] = true;
+ dump_elements = true;
+
+ /*
+ * Collect maximum frequency. The sub-packages are sorted in
+ * descending power dissipation order, so one would assume that
+ * the highest frequency is first. However, it is not clear
+ * from the ACPI spec that this is necessarily an assumption we
+ * should make, so instead we should probably scan through all
+ * the valid sub-packages and find the highest frequency.
+ */
+ if (max_freq < pstate->Package.Elements[0].Integer.Value) {
+ max_freq = pstate->Package.Elements[0].Integer.Value;
+ max_freq_valid = true;
+ }
+
+ /* Sanity check descending power dissipation levels */
+ if ((i > 0) && (prev_power != 0) &&
+ (pstate->Package.Elements[1].Integer.Value > prev_power)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSSSubPackagePowerNotDecending",
+ "%s P-State sub-package %" PRIu32 " has a larger "
+ "power dissipation setting than the previous "
+ "sub-package.", name, i);
+ fwts_advice(fw,
+ "_PSS P-States must be ordered in decending "
+ "order of power dissipation, so that the "
+ "zero'th entry has the highest power "
+ "dissipation level and the Nth has the "
+ "lowest.");
+ failed = true;
+ }
+ prev_power = pstate->Package.Elements[1].Integer.Value;
+ }
+
+ /*
+ * If we have some valid data then dump it out, it is useful to see
+ */
+ if (dump_elements) {
+ fwts_log_info_verbatim(fw, "%s values:", name);
+ fwts_log_info_verbatim(fw, "P-State Freq Power Latency Bus Master");
+ fwts_log_info_verbatim(fw, " (MHz) (mW) (us) Latency (us)");
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pstate = &obj->Package.Elements[i];
+ if (element_ok[i]) {
+ fwts_log_info_verbatim(fw, " %3d %7" PRIu64 " %8" PRIu64
+ " %5" PRIu64 " %5" PRIu64,
+ i,
+ (uint64_t)pstate->Package.Elements[0].Integer.Value,
+ (uint64_t)pstate->Package.Elements[1].Integer.Value,
+ (uint64_t)pstate->Package.Elements[2].Integer.Value,
+ (uint64_t)pstate->Package.Elements[3].Integer.Value);
+ } else {
+ fwts_log_info_verbatim(fw,
+ " %3d ---- ----- -- -- (invalid)", i);
+ }
+ }
+ }
+
+ free(element_ok);
+
+ /*
+ * Sanity check maximum frequency. We could also check the DMI data
+ * for a BIOS date (but this can be wrong) or check the CPU identity
+ * (which requires adding in new CPU identity checks) to make a decision
+ * on when it is reasonable to assume a CPU is modern and hence clocked
+ * incorrectly. For now, just flag up a low level error that the
+ * frequency looks rather low rather than try to be intelligent (and
+ * possibly make a mistake). I'd rather flag up a few false positives
+ * on older machines than miss flagging up bad _PSS settings on new
+ * machines.
+ */
+ if (max_freq_valid && max_freq < 1000) {
+ fwts_failed(fw, LOG_LEVEL_LOW, "Method_PSSSubPackageLowFreq",
+ "Maximum CPU frequency is %dHz and this is low for "
+ "a modern processor. This may indicate the _PSS "
+ "P-States are incorrect\n", max_freq);
+ fwts_advice(fw,
+ "The _PSS P-States are used by the Linux CPU frequency "
+ "driver to set the CPU frequencies according to system "
+ "load. Sometimes the firmware sets these incorrectly "
+ "and the machine runs at a sub-optimal speed. One can "
+ "view the firmware defined CPU frequencies via "
+ "/sys/devices/system/cpu/cpu*/cpufreq/"
+ "scaling_available_frequencies");
+ failed = true;
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PSS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL, "_PSS", NULL, 0, method_test_PSS_return, NULL);
+}
+
+static int method_test_PPC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PPC", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_PPE(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PPE", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_PSD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PSD", obj, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ pkg = &obj->Package.Elements[i];
+ if (pkg->Package.Count != 5) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSDSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 5 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ /* Elements in Sub-packages are integers */
+ for (j = 0; j < 5; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSDBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PSD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSD", NULL, 0, method_test_PSD_return, NULL);
+}
+
+static int method_test_PDL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PDL", NULL, 0, method_test_integer_return, NULL);
+}
+
+
+static void method_test_PTC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PTC", obj, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_PTC", obj, 2) != FWTS_OK)
+ return;
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_RESOURCE *resource;
+ ACPI_STATUS status;
+ ACPI_OBJECT *element_buf = &obj->Package.Elements[i];
+
+ status = AcpiBufferToResource(element_buf->Buffer.Pointer, element_buf->Buffer.Length, &resource);
+ if (ACPI_FAILURE(status)) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_PTCBadElement",
+ "%s should contain only Resource Descriptors", name);
+ continue;
+ }
+
+ if (resource->Type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_PTCBadElement",
+ "%s should contain only Resource Type 16, got %" PRIu32 "\n",
+ name, resource->Type);
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PTC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PTC", NULL, 0, method_test_PTC_return, NULL);
+}
+
+static int method_test_TDL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TDL", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_TPC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TPC", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_TSD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Something is really wrong if we don't have any elements in _TSD */
+ if (method_package_count_min(fw, name, "_TSD", obj, 1) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDElementType",
+ "%s package element %" PRIu32
+ " was not a package.", name, i);
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ pkg = &obj->Package.Elements[i];
+ /*
+ * Currently we expect a package of 5 integers.
+ */
+ if (pkg->Package.Count != 5) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 5 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ for (j = 0; j < 5; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElementCount",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.", name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+
+ /* Element 0 must equal the number elements in the package */
+ if (pkg->Package.Elements[0].Integer.Value != pkg->Package.Count) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElement0",
+ "%s sub-package %" PRIu32
+ " element 0 (NumEntries) "
+ "was expected to have value 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+ /* Element 1 should contain zero */
+ if (pkg->Package.Elements[1].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElement1",
+ "%s sub-package %" PRIu32
+ " element 1 (Revision) "
+ "was expected to have value 1, instead it "
+ "was 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[1].Integer.Value);
+ failed = true;
+ }
+ /* Element 3 should contain 0xfc..0xfe */
+ if ((pkg->Package.Elements[3].Integer.Value != 0xfc) &&
+ (pkg->Package.Elements[3].Integer.Value != 0xfd) &&
+ (pkg->Package.Elements[3].Integer.Value != 0xfe)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElement1",
+ "%s sub-package %" PRIu32
+ " element 3 (CoordType) "
+ "was expected to have value 0xfc (SW_ALL), "
+ "0xfd (SW_ANY) or 0xfe (HW_ALL), instead it "
+ "was 0x%" PRIx64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[3].Integer.Value);
+ failed = true;
+ }
+ /* Element 4 number of processors, skip check */
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_TSD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TSD", NULL, 0, method_test_TSD_return, NULL);
+}
+
+static void method_test_TSS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+ bool *tss_elements_ok;
+ bool an_element_ok = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Something is really wrong if we don't have any elements in _TSS */
+ if (method_package_count_min(fw, name, "_TSS", obj, 1) != FWTS_OK)
+ return;
+
+ tss_elements_ok = calloc(obj->Package.Count, sizeof(bool));
+ if (tss_elements_ok == NULL) {
+ fwts_log_error(fw, "Cannot allocate an array. Test aborted.");
+ return;
+ }
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+
+ tss_elements_ok[i] = true;
+
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSSElementType",
+ "%s package element %" PRIu32
+ " was not a package.", name, i);
+ tss_elements_ok[i] = false;
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ pkg = &obj->Package.Elements[i];
+ /*
+ * We expect a package of 5 integers.
+ */
+ if (pkg->Package.Count != 5) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSSSubPackageElementCount",
+ "%s sub-package %" PRIu32
+ " was expected to have 5 elements, "
+ "got %" PRIu32" elements instead.",
+ name, i, pkg->Package.Count);
+ tss_elements_ok[i] = false;
+ failed = true;
+ continue; /* Skip processing sub-package */
+ }
+
+ for (j = 0; j < 5; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSSSubPackageElementCount",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.", name, i, j);
+ tss_elements_ok[i] = false;
+ }
+ }
+ if (!tss_elements_ok[i]) {
+ failed = true;
+ continue;
+ }
+
+ /* At least one element is OK, so remember that */
+ an_element_ok = true;
+
+ /* Element 0 must be 1..100 */
+ if ((pkg->Package.Elements[0].Integer.Value < 1) ||
+ (pkg->Package.Elements[0].Integer.Value > 100)) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TSDSubPackageElement0",
+ "%s sub-package %" PRIu32 " element 0"
+ "was expected to have value 1..100, instead "
+ "was %" PRIu64 ".",
+ name, i,
+ (uint64_t)pkg->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+ /* Skip checking elements 1..4 */
+ }
+
+ /* Summary info */
+ if (an_element_ok) {
+ fwts_log_info_verbatim(fw, "%s values:", name);
+ fwts_log_info_verbatim(fw, "T-State CPU Power Latency Control Status");
+ fwts_log_info_verbatim(fw, " Freq (mW) (usecs)");
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (tss_elements_ok[i]) {
+ ACPI_OBJECT *pkg = &obj->Package.Elements[i];
+
+ fwts_log_info_verbatim(fw,
+ " %3d %3" PRIu64 "%% %7" PRIu64 " %7" PRIu64
+ " %2.2" PRIx64 " %2.2" PRIx64, i,
+ (uint64_t)pkg->Package.Elements[0].Integer.Value,
+ (uint64_t)pkg->Package.Elements[1].Integer.Value,
+ (uint64_t)pkg->Package.Elements[2].Integer.Value,
+ (uint64_t)pkg->Package.Elements[3].Integer.Value,
+ (uint64_t)pkg->Package.Elements[4].Integer.Value);
+ } else {
+ fwts_log_info_verbatim(fw,
+ " %3d ---- ----- ----- -- -- (invalid)", i);
+ }
+ }
+ }
+ free(tss_elements_ok);
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_TSS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TSS", NULL, 0, method_test_TSS_return, NULL);
+}
+
+/*
+ * Section 8.4.4 Lower Power Idle States
+*/
+
+static void method_test_LPI_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i, j;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_min(fw, name, "_LPI", obj, 3) != FWTS_OK)
+ return;
+
+ /* first 3 elements are integers, and rests are packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (i < 3) {
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadElementType",
+ "%s element %" PRIu32 " is not an integer.", name, i);
+ failed = true;
+ continue;
+ }
+
+ if (i == 0) {
+ if (obj->Package.Elements[i].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadRevision",
+ "%s: Expected Revision to be 0, "
+ "got 0x%4.4" PRIx64 ".", name,
+ (uint64_t)obj->Package.Elements[i].Integer.Value);
+ failed = true;
+ }
+ } else if (i == 2) {
+ if (obj->Package.Elements[i].Integer.Value != obj->Package.Count - 3) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadCount",
+ "%s Count reports %" PRIu32 ", but there are %" PRIu32 " sub-packages.",
+ name, (uint32_t) obj->Package.Elements[i].Integer.Value,
+ obj->Package.Count - 3);
+ failed = true;
+ }
+ }
+ } else {
+ ACPI_OBJECT *pkg;
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadElementType",
+ "%s element %" PRIu32 " is not a package.", name, i);
+ failed = true;
+ continue;
+ }
+
+ pkg = &obj->Package.Elements[i];
+ for (j = 0; j < pkg->Package.Count; j++) {
+ switch (j) {
+ case 0 ... 5:
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadESublementType",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " is not "
+ "an integer.", name, i, j);
+ failed = true;
+ }
+ break;
+ case 6:
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER &&
+ pkg->Package.Elements[j].Type != ACPI_TYPE_BUFFER) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadESublementType",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " is not "
+ "a buffer or an integer.", name, i, j);
+ failed = true;
+ }
+ break;
+ case 7 ... 8:
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_BUFFER) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadESublementType",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " is not "
+ "a buffer.", name, i, j);
+ failed = true;
+ }
+ break;
+ case 9:
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_STRING) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadESublementType",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " is not "
+ "a string.", name, i, j);
+ failed = true;
+ }
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_LPIBadESublement",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " should have "
+ "9 elements, got .", name, i, j+1);
+ failed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_LPI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_LPI", NULL, 0, method_test_LPI_return, NULL);
+}
+
+static void method_test_RDI_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i, j;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* First element is Revision */
+ if (obj->Package.Elements[0].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_RDIBadID",
+ "%s: Expected Revision to be 0, "
+ "got 0x%4.4" PRIx64 ".", name,
+ (uint64_t)obj->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+
+ /* The rest of elements are packages with references */
+ for (i = 1; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ pkg = &obj->Package.Elements[i];
+
+ if (pkg->Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_RDIBadElementType",
+ "%s element %" PRIu32 " is not a package.", name, i);
+ failed = true;
+ continue;
+ }
+
+ for (j = 0; j < pkg->Package.Count; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_RDIBadESublementType",
+ "%s sub-package %" PRIu32 " element %" PRIu32 " is not "
+ "a Reference.", name, i, j);
+ failed = true;
+ }
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_RDI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_RDI", NULL, 0, method_test_RDI_return, NULL);
+}
+
+/*
+ * Section 8.5 Processor Aggregator Device
+ */
+
+static void method_test_PUR_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ static fwts_package_element elements[] = {
+ { ACPI_TYPE_INTEGER, "RevisionID" },
+ { ACPI_TYPE_INTEGER, "NumProcessors" },
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_PUR", obj, 2) != FWTS_OK)
+ return;
+
+ if (method_package_elements_type(fw, name, "_PUR", obj, elements, 2) != FWTS_OK)
+ return;
+
+ /* RevisionID */
+ if (obj->Package.Elements[0].Integer.Value != 1) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PURBadID",
+ "%s: Expected RevisionID to be 1, "
+ "got 0x%8.8" PRIx64 ".", name,
+ (uint64_t)obj->Package.Elements[0].Integer.Value);
+ return;
+ }
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PUR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PUR", NULL, 0, method_test_PUR_return, NULL);
+}
+
+/*
+ * Section 9.1 System Indicators
+ */
+
+static int method_test_SST(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ int ret, i;
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ for (i = 0; i <= 4; i++) {
+ arg[0].Integer.Value = i;
+ ret = method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SST", arg, 1, method_test_NULL_return, NULL);
+
+ if (ret != FWTS_OK)
+ break;
+ }
+ return ret;
+}
+
+static int method_test_MSG(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 0;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_MSG", arg, 1, method_test_NULL_return, NULL);
+}
+
+/*
+ * Section 9.2 Ambient Light Sensor Device
+ */
+method_test_integer(_ALC, METHOD_OPTIONAL)
+method_test_integer(_ALI, METHOD_OPTIONAL)
+method_test_integer(_ALT, METHOD_OPTIONAL)
+
+static void method_test_ALR_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+ uint32_t adjustment = 0, illuminance = 0;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Could be one or more sub-packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+
+ pkg = &obj->Package.Elements[i];
+ if (pkg->Package.Count != 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ALRBadSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 2 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ } else {
+ /* elements should be listed in monotonically increasing order */
+ if (pkg->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
+ adjustment > pkg->Package.Elements[0].Integer.Value) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ALRBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 0 is an invalid integer.",
+ name, i);
+ failed = true;
+ }
+
+ if (pkg->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
+ illuminance > pkg->Package.Elements[1].Integer.Value) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ALRBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element 1 is an invalid integer.",
+ name, i);
+ failed = true;
+ }
+ adjustment = pkg->Package.Elements[0].Integer.Value;
+ illuminance = pkg->Package.Elements[1].Integer.Value;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_ALR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ALR", NULL, 0, method_test_ALR_return, NULL);
+}
+
+static int method_test_ALP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ALP", NULL, 0, method_test_polling_return, "_ALP");
+}
+
+
+/*
+ * Section 9.4 Lid control
+ */
+static void method_test_LID_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK)
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_LID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_LID", NULL, 0, method_test_LID_return, NULL);
+}
+
+
+/*
+ * Section 9.8 ATA Controllers
+ */
+
+static void method_test_GTF_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ if (obj->Buffer.Length % 7)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_GTFBadBufferSize",
+ "%s should return a buffer with size of multiple of 7.",
+ name);
+ else
+ method_passed_sane(fw, name, "buffer");
+}
+
+static int method_test_GTF(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GTF", NULL, 0, method_test_GTF_return, NULL);
+}
+
+static void method_test_GTM_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ if (obj->Buffer.Length != (5 * sizeof(uint32_t)))
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_GTMBadBufferSize",
+ "%s should return a buffer with size of 20.",
+ name);
+ else
+ method_passed_sane(fw, name, "buffer");
+}
+
+static int method_test_GTM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GTM", NULL, 0, method_test_GTM_return, NULL);
+}
+
+/*
+ * Section 9.12 Memory Devices
+ */
+
+static void method_test_MBM_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ static fwts_package_element elements[] = {
+ { ACPI_TYPE_INTEGER, "Revision" },
+ { ACPI_TYPE_INTEGER, "Window Size" },
+ { ACPI_TYPE_INTEGER, "Sampling Interval" },
+ { ACPI_TYPE_INTEGER, "Maximum Bandwidth" },
+ { ACPI_TYPE_INTEGER, "Average Bandwidth" },
+ { ACPI_TYPE_INTEGER, "Low Bandwidth" },
+ { ACPI_TYPE_INTEGER, "Low Notification Threshold" },
+ { ACPI_TYPE_INTEGER, "High Notification Threshold" },
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_MBM", obj, 8) != FWTS_OK)
+ return;
+
+ /* For now, just check types */
+ if (method_package_elements_type(fw, name, "_MBM", obj, elements, 8) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_MBM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_MBM", NULL, 0, method_test_MBM_return, NULL);
+}
+
+/*
+ * Section 9.13 USB Port Capabilities
+ */
+
+static void method_test_UPC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i, connector_type;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_UPC", obj, 4) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_UPC", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ connector_type = obj->Package.Elements[1].Integer.Value;
+ if (connector_type > 0x0a && connector_type < 0xFF) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_UPCBadReturnType",
+ "%s element 1 returned reserved value.", name);
+ return;
+ }
+
+ for (i = 2; i < 4; i++) {
+ if (obj->Package.Elements[i].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_UPCBadReturnType",
+ "%s element %" PRIu32 " is not zero.", name, i);
+ return;
+ }
+ }
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_UPC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_UPC", NULL, 0, method_test_UPC_return, NULL);
+}
+
+/*
+ * Section 9.16 User Presence Detection Device
+ */
+
+static int method_test_UPD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_UPD", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_UPP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_UPP", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 9.18 Wake Alarm Device
+ */
+static void method_test_GCP_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value & ~0x1f)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_GCPReturn",
+ "%s returned %" PRId64 ", should be between 0 and 31, "
+ "one or more of the reserved bits 5..31 seem "
+ "to be set.",
+ name, (uint64_t)obj->Integer.Value);
+ else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_GCP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GCP", NULL, 0, method_test_GCP_return, "_GCP");
+}
+
+static void method_test_GRT_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) != FWTS_OK)
+ return;
+
+ if (obj->Buffer.Length != 16) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_GRTBadBufferSize",
+ "%s should return a buffer of 16 bytes, but "
+ "instead just returned %" PRIu32,
+ name, obj->Buffer.Length);
+ return;
+ }
+
+ /*
+ * Should sanity check this, but we can't read the
+ * the data in this emulated mode, so ignore
+ */
+ method_passed_sane(fw, name, "buffer");
+}
+
+static int method_test_GRT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GRT", NULL, 0, method_test_GRT_return, NULL);
+}
+
+static void method_test_GWS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value & ~0x3)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_GWSReturn",
+ "%s returned %" PRIu64 ", should be between 0 and 3, "
+ "one or more of the reserved bits 2..31 seem "
+ "to be set.",
+ name, (uint64_t)obj->Integer.Value);
+ else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_GWS(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1; /* DC timer */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GWS", arg, 1, method_test_GWS_return, "_GWS");
+}
+
+static void method_test_CWS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value != 0 && obj->Integer.Value != 1)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_CWSInvalidInteger",
+ "%s returned %" PRIu64 ", should be 0 or 1.",
+ name, (uint64_t)obj->Integer.Value);
+ else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_CWS(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ int i, ret;
+ arg[0].Type = ACPI_TYPE_INTEGER;
+
+ for (i = 0; i < 2; i++) {
+ arg[0].Integer.Value = i;
+ ret = method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CWS", arg, 1, method_test_CWS_return, NULL);
+
+ if (ret != FWTS_OK)
+ break;
+ }
+ return ret;
+}
+
+static void method_test_SRT_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value & ~0x1)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_SRTReturn",
+ "%s returned %" PRId64 ", should be between 0 and 1, "
+ "one or more of the reserved bits 1..31 seem "
+ "to be set.",
+ name, (uint64_t)obj->Integer.Value);
+ else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_SRT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SRT", NULL, 0, method_test_SRT_return, NULL);
+}
+
+static int method_test_STP(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[2];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1; /* DC timer */
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 0; /* wake up instantly */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_STP", arg, 2, method_test_passed_failed_return, "_STP");
+}
+
+static int method_test_STV(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[2];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1; /* DC timer */
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 100; /* timer value */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_STV", arg, 2, method_test_passed_failed_return, "_STV");
+}
+
+static int method_test_TIP(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1; /* DC timer */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TIP", arg, 1, method_test_integer_return, NULL);
+}
+
+static int method_test_TIV(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1; /* DC timer */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TIV", arg, 1, method_test_integer_return, NULL);
+}
+
+
+/*
+ * Section 10.1 Smart Battery
+ */
+static void method_test_SBS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ static char *sbs_info[] = {
+ "Maximum 1 Smart Battery, system manager/selector not present",
+ "Maximum 1 Smart Battery, system manager/selector present",
+ "Maximum 2 Smart Batteries, system manager/selector present",
+ "Maximum 3 Smart Batteries, system manager/selector present",
+ "Maximum 4 Smart Batteries, system manager/selector present"
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ switch (obj->Integer.Value) {
+ case 0 ... 4:
+ fwts_passed(fw, "%s correctly returned value %" PRIu64 " %s",
+ name, (uint64_t)obj->Integer.Value,
+ sbs_info[obj->Integer.Value]);
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_SBSReturn",
+ "%s returned %" PRIu64 ", should be between 0 and 4.",
+ name, (uint64_t)obj->Integer.Value);
+ fwts_advice(fw,
+ "Smart Battery %s is incorrectly informing "
+ "the OS about the smart battery "
+ "configuration. This is a bug and needs to be "
+ "fixed.", name);
+ break;
+ }
+}
+
+static int method_test_SBS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_SBS", NULL, 0, method_test_SBS_return, NULL);
+}
+
+
+/*
+ * Section 10.2 Battery Control Methods
+ */
+static int method_test_BCT(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 50; /* 50% */
+
+ /*
+ * For now, just check that we get some integer back, values
+ * can be 0x00000000, 0x00000001-0xfffffffe and 0xffffffff,
+ * so anything is valid as long as it is an integer
+ */
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BCT", arg, 1, method_test_integer_return, NULL);
+}
+
+static void method_test_BIF_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ bool failed = false;
+
+ static fwts_package_element elements[] = {
+ { ACPI_TYPE_INTEGER, "Power Unit" },
+ { ACPI_TYPE_INTEGER, "Design Capacity" },
+ { ACPI_TYPE_INTEGER, "Last Full Charge Capacity" },
+ { ACPI_TYPE_INTEGER, "Battery Technology" },
+ { ACPI_TYPE_INTEGER, "Design Voltage" },
+ { ACPI_TYPE_INTEGER, "Design Capacity of Warning" },
+ { ACPI_TYPE_INTEGER, "Design Capactty of Low" },
+ { ACPI_TYPE_INTEGER, "Battery Capacity Granularity 1" },
+ { ACPI_TYPE_INTEGER, "Battery Capacity Granularity 2" },
+ { ACPI_TYPE_STRING, "Model Number" },
+ { ACPI_TYPE_STRING, "Serial Number" },
+ { ACPI_TYPE_STRING, "Battery Type" },
+ { ACPI_TYPE_STRING, "OEM Information" }
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_BIF", obj, 13) != FWTS_OK)
+ return;
+
+ if (method_package_elements_type(fw, name, "_BIF", obj, elements, 13) != FWTS_OK)
+ return;
+
+ /* Sanity check each field */
+ /* Power Unit */
+ if (obj->Package.Elements[0].Integer.Value > 0x00000002) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BIFBadUnits",
+ "%s: Expected Power Unit (Element 0) to be "
+ "0 (mWh) or 1 (mAh), got 0x%8.8" PRIx64 ".",
+ name, (uint64_t)obj->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+#ifdef FWTS_METHOD_PEDANDTIC
+ /*
+ * Since this information may be evaluated by communicating with
+ * the EC we skip these checks as we can't do this from userspace
+ */
+ /* Design Capacity */
+ if (obj->Package.Elements[1].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIFBadCapacity",
+ "%s: Design Capacity (Element 1) is "
+ "unknown: 0x%8.8" PRIx64 ".",
+ name, obj->Package.Elements[1].Integer.Value);
+ failed = true;
+ }
+ /* Last Full Charge Capacity */
+ if (obj->Package.Elements[2].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIFChargeCapacity",
+ "%s: Last Full Charge Capacity (Element 2) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, obj->Package.Elements[2].Integer.Value);
+ failed = true;
+ }
+#endif
+ /* Battery Technology */
+ if (obj->Package.Elements[3].Integer.Value > 0x00000002) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BIFBatTechUnit",
+ "%s: Expected Battery Technology Unit "
+ "(Element 3) to be 0 (Primary) or 1 "
+ "(Secondary), got 0x%8.8" PRIx64 ".",
+ name, (uint64_t)obj->Package.Elements[3].Integer.Value);
+ failed = true;
+ }
+#ifdef FWTS_METHOD_PEDANDTIC
+ /*
+ * Since this information may be evaluated by communicating with
+ * the EC we skip these checks as we can't do this from userspace
+ */
+ /* Design Voltage */
+ if (obj->Package.Elements[4].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIFDesignVoltage",
+ "%s: Design Voltage (Element 4) is "
+ "unknown: 0x%8.8" PRIx64 ".",
+ name, obj->Package.Elements[4].Integer.Value);
+ failed = true;
+ }
+ /* Design capacity warning */
+ if (obj->Package.Elements[5].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIFDesignCapacityE5",
+ "%s: Design Capacity Warning (Element 5) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, obj->Package.Elements[5].Integer.Value);
+ failed = true;
+ }
+ /* Design capacity low */
+ if (obj->Package.Elements[6].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIFDesignCapacityE6",
+ "%s: Design Capacity Warning (Element 6) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, obj->Package.Elements[6].Integer.Value);
+ failed = true;
+ }
+#endif
+ if (failed)
+ fwts_advice(fw,
+ "Battery %s package contains errors. It is "
+ "worth running the firmware test suite "
+ "interactive 'battery' test to see if this "
+ "is problematic. This is a bug an needs to "
+ "be fixed.", name);
+ else
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_BIF(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BIF", NULL, 0, method_test_BIF_return, NULL);
+}
+
+static void method_test_BIX_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ bool failed = false;
+
+ static fwts_package_element elements[] = {
+ { ACPI_TYPE_INTEGER, "Revision" },
+ { ACPI_TYPE_INTEGER, "Power Unit" },
+ { ACPI_TYPE_INTEGER, "Design Capacity" },
+ { ACPI_TYPE_INTEGER, "Last Full Charge Capacity" },
+ { ACPI_TYPE_INTEGER, "Battery Technology" },
+ { ACPI_TYPE_INTEGER, "Design Voltage" },
+ { ACPI_TYPE_INTEGER, "Design Capacity of Warning" },
+ { ACPI_TYPE_INTEGER, "Design Capactty of Low" },
+ { ACPI_TYPE_INTEGER, "Cycle Count" },
+ { ACPI_TYPE_INTEGER, "Measurement Accuracy" },
+ { ACPI_TYPE_INTEGER, "Max Sampling Time" },
+ { ACPI_TYPE_INTEGER, "Min Sampling Time" },
+ { ACPI_TYPE_INTEGER, "Max Averaging Interval" },
+ { ACPI_TYPE_INTEGER, "Min Averaging Interval" },
+ { ACPI_TYPE_INTEGER, "Battery Capacity Granularity 1" },
+ { ACPI_TYPE_INTEGER, "Battery Capacity Granularity 2" },
+ { ACPI_TYPE_STRING, "Model Number" },
+ { ACPI_TYPE_STRING, "Serial Number" },
+ { ACPI_TYPE_STRING, "Battery Type" },
+ { ACPI_TYPE_STRING, "OEM Information" }
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_BIX", obj, 20) != FWTS_OK)
+ return;
+
+ if (method_package_elements_type(fw, name, "_BIX", obj, elements, 20) != FWTS_OK)
+ return;
+
+ /* Sanity check each field */
+ /* Power Unit */
+ if (obj->Package.Elements[1].Integer.Value > 0x00000002) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BIXPowerUnit",
+ "%s: Expected %s (Element 1) to be "
+ "0 (mWh) or 1 (mAh), got 0x%8.8" PRIx64 ".",
+ name, elements[1].name,
+ (uint64_t)obj->Package.Elements[1].Integer.Value);
+ failed = true;
+ }
+#ifdef FWTS_METHOD_PEDANDTIC
+ /*
+ * Since this information may be evaluated by communicating with
+ * the EC we skip these checks as we can't do this from userspace
+ */
+ /* Design Capacity */
+ if (obj->Package.Elements[2].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIXDesignCapacity",
+ "%s: %s (Element 2) is "
+ "unknown: 0x%8.8" PRIx64 ".",
+ name, elements[2].name,
+ obj->Package.Elements[2].Integer.Value);
+ failed = true;
+ }
+ /* Last Full Charge Capacity */
+ if (obj->Package.Elements[3].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIXFullChargeCapacity",
+ "%s: %s (Element 3) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, elements[3].name,
+ obj->Package.Elements[3].Integer.Value);
+ failed = true;
+ }
+#endif
+ /* Battery Technology */
+ if (obj->Package.Elements[4].Integer.Value > 0x00000002) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BIXBatteryTechUnit",
+ "%s: %s "
+ "(Element 4) to be 0 (Primary) or 1 "
+ "(Secondary), got 0x%8.8" PRIx64 ".",
+ name, elements[4].name,
+ (uint64_t)obj->Package.Elements[4].Integer.Value);
+ failed = true;
+ }
+#ifdef FWTS_METHOD_PEDANDTIC
+ /*
+ * Since this information may be evaluated by communicating with
+ * the EC we skip these checks as we can't do this from userspace
+ */
+ /* Design Voltage */
+ if (obj->Package.Elements[5].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIXDesignVoltage",
+ "%s: %s (Element 5) is unknown: "
+ "0x%8.8" PRIx64 ".",
+ name, elements[5].name,
+ obj->Package.Elements[5].Integer.Value);
+ failed = true;
+ }
+ /* Design capacity warning */
+ if (obj->Package.Elements[6].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIXDesignCapacityE6",
+ "%s: %s (Element 6) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, elements[6].name,
+ obj->Package.Elements[6].Integer.Value);
+ failed = true;
+ }
+ /* Design capacity low */
+ if (obj->Package.Elements[7].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "Method_BIXDesignCapacityE7",
+ "%s: %s (Element 7) "
+ "is unknown: 0x%8.8" PRIx64 ".",
+ name, elements[7].name,
+ obj->Package.Elements[7].Integer.Value);
+ failed = true;
+ }
+ /* Cycle Count */
+ if (obj->Package.Elements[8].Integer.Value > 0x7fffffff) {
+ fwts_failed(fw, LOG_LEVEL_LOW, "Method_BIXCyleCount",
+ "%s: %s (Element 8) is unknown: "
+ "0x%8.8" PRIx64 ".", Elements[8].name,
+ name, obj->Package.Elements[8].Integer.Value);
+ failed = true;
+ }
+#endif
+ if (failed)
+ fwts_advice(fw,
+ "Battery %s package contains errors. It is "
+ "worth running the firmware test suite "
+ "interactive 'battery' test to see if this "
+ "is problematic. This is a bug an needs to "
+ "be fixed.", name);
+ else
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_BIX(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BIX", NULL, 0, method_test_BIX_return, NULL);
+}
+
+static int method_test_BMA(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BMA", arg, 1, method_test_integer_return, NULL);
+}
+
+static int method_test_BMS(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 1;
+
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BMS", arg, 1, method_test_integer_return, NULL);
+}
+
+static void method_test_BST_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_BST", obj, 4) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_BST", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ /* Sanity check each field */
+ /* Battery State */
+ if ((obj->Package.Elements[0].Integer.Value) > 7) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BSTBadState",
+ "%s: Expected Battery State (Element 0) to "
+ "be 0..7, got 0x%8.8" PRIx64 ".",
+ name, (uint64_t)obj->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+ /* Ensure bits 0 (discharging) and 1 (charging) are not both set, see 10.2.2.6 */
+ if (((obj->Package.Elements[0].Integer.Value) & 3) == 3) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BSTBadState",
+ "%s: Battery State (Element 0) is "
+ "indicating both charging and discharginng "
+ "which is not allowed. Got value 0x%8.8" PRIx64 ".",
+ name, (uint64_t)obj->Package.Elements[0].Integer.Value);
+ failed = true;
+ }
+ /* Battery Present Rate - cannot check, pulled from EC */
+ /* Battery Remaining Capacity - cannot check, pulled from EC */
+ /* Battery Present Voltage - cannot check, pulled from EC */
+ if (failed)
+ fwts_advice(fw,
+ "Battery %s package contains errors. It is "
+ "worth running the firmware test suite "
+ "interactive 'battery' test to see if this "
+ "is problematic. This is a bug an needs to "
+ "be fixed.", name);
+ else
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_BST(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BST", NULL, 0, method_test_BST_return, NULL);
+}
+
+static int method_test_BTP(fwts_framework *fw)
+{
+ static int values[] = { 0, 1, 100, 200, 0x7fffffff };
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = values[i];
+ if (method_evaluate_method(fw, METHOD_MOBILE, "_BTP", arg, 1,
+ method_test_NULL_return, NULL) == FWTS_NOT_EXIST)
+ break;
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+static void method_test_PCL_return(fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(fw);
+ FWTS_UNUSED(name);
+ FWTS_UNUSED(buf);
+ FWTS_UNUSED(obj);
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PCL", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ fwts_passed(fw, "%s returned a sane package of %" PRIu32 " references.", name, obj->Package.Count);
+}
+
+static int method_test_PCL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_PCL", NULL, 0, method_test_PCL_return, "_PCL");
+}
+
+static int method_test_BTH(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ int i, ret;
+ arg[0].Type = ACPI_TYPE_INTEGER;
+
+ for (i = 0; i <= 100; i++) {
+ arg[0].Integer.Value = i;
+ ret = method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_BTH", arg, 1, method_test_NULL_return, NULL);
+
+ if (ret != FWTS_OK)
+ break;
+ }
+ return ret;
+}
+
+static int method_test_BTM(fwts_framework *fw)
+{
+ static int values[] = { 0, 1, 100, 200, 0x7fffffff };
+ int i;
+
+ for (i = 0 ; i < 5; i++) {
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = values[i];
+ if (method_evaluate_method(fw, METHOD_MOBILE, "_BTM", arg, 1,
+ method_test_NULL_return, NULL) == FWTS_NOT_EXIST)
+ break;
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+static void method_test_BMD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_BMD", obj, 5) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_BMD", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ fwts_acpi_object_dump(fw, obj);
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_BMD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MOBILE,
+ "_BMD", NULL, 0, method_test_BMD_return, NULL);
+}
+
+static int method_test_BMC(fwts_framework *fw)
+{
+ static int values[] = { 0, 1, 2, 4 };
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = values[i];
+ if (method_evaluate_method(fw, METHOD_MOBILE, "_BMC", arg, 1,
+ method_test_NULL_return, NULL) == FWTS_NOT_EXIST)
+ break;
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+
+/*
+ * Section 10.3 AC Adapters and Power Sources Objects
+ */
+static void method_test_PRL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PRL", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PRL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PRL", NULL, 0, method_test_PRL_return, NULL);
+}
+
+static void method_test_PSR_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value > 2) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PSRZeroOrOne",
+ "%s returned 0x%8.8" PRIx64 ", expected 0 "
+ "(offline) or 1 (online)",
+ name, (uint64_t)obj->Integer.Value);
+ } else
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_PSR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSR", NULL, 0, method_test_PSR_return, NULL);
+}
+
+static void method_test_PIF_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ static fwts_package_element elements[] = {
+ { ACPI_TYPE_INTEGER, "Power Source State" },
+ { ACPI_TYPE_INTEGER, "Maximum Output Power" },
+ { ACPI_TYPE_INTEGER, "Maximum Input Power" },
+ { ACPI_TYPE_STRING, "Model Number" },
+ { ACPI_TYPE_STRING, "Serial Number" },
+ { ACPI_TYPE_STRING, "OEM Information" }
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_PIF", obj, 6) != FWTS_OK)
+ return;
+
+ if (method_package_elements_type(fw, name, "_PIF", obj, elements, 6) != FWTS_OK)
+ return;
+
+ fwts_acpi_object_dump(fw, obj);
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PIF(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PIF", NULL, 0, method_test_PIF_return, NULL);
+}
+
+/*
+ * Section 10.4 Power Meters
+ */
+
+static int method_test_GAI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GAI", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_GHL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GHL", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_PMC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+ ACPI_OBJECT *element;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_PMC", obj, 14) != FWTS_OK)
+ return;
+
+ /* check element types */
+ for (i = 0; i < 14; i++) {
+ element = &obj->Package.Elements[i];
+ if (i > 10) {
+ if (element->Type != ACPI_TYPE_STRING) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElementType",
+ "%s element %" PRIu32 " is not a string.", name, i);
+ failed = true;
+ }
+ } else {
+ if (element->Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElementType",
+ "%s element %" PRIu32 " is not an integer.", name, i);
+ failed = true;
+ }
+ }
+ }
+
+ /* check element's constraints */
+ element = &obj->Package.Elements[0];
+ if (element->Integer.Value & 0xFFFFFEF0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElement",
+ "%s element 0 has reserved bits that are non-zero, got "
+ "0x%" PRIx32 " and expected 0 for these field. ", name,
+ (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ element = &obj->Package.Elements[1];
+ if (element->Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElement",
+ "%s element 1 is expected to be 0, got 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ element = &obj->Package.Elements[2];
+ if (element->Integer.Value != 0 && element->Integer.Value != 1) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElement",
+ "%s element 2 is expected to be 0 or 1, got 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ element = &obj->Package.Elements[3];
+ if (element->Integer.Value > 100000) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElement",
+ "%s element 3 exceeds 100000 (100 percent) = 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ /* nothing to check for elements 4~7 */
+
+ element = &obj->Package.Elements[8];
+ if (element->Integer.Value != 0 && element->Integer.Value != 0xFFFFFFFF) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_PMCBadElement",
+ "%s element 8 is expected to be 0 or 1, got 0x%" PRIx32 ".",
+ name, (uint32_t) element->Integer.Value);
+ failed = true;
+ }
+
+ /* nothing to check for elements 9~13 */
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PMC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PMC", NULL, 0, method_test_PMC_return, NULL);
+}
+
+static void method_test_PMD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PMD", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PMD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PMD", NULL, 0, method_test_PMD_return, NULL);
+}
+
+static int method_test_PMM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PMM", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 10.5 Wireless Power Controllers
+ */
+static void method_test_WPC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Integer.Value <= 0x02 || obj->Integer.Value == 0xff)
+ method_passed_sane(fw, name, "integer");
+ else
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "Method_WPCInvalidInteger",
+ "%s returned an invalid integer 0x%8.8" PRIx64,
+ name, (uint64_t)obj->Integer.Value);
+}
+
+static int method_test_WPC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_WPC", NULL, 0, method_test_WPC_return, "_WPC");
+}
+
+static int method_test_WPP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_WPP", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 11.3 Fan Devices
+ */
+static void method_test_FIF_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_FIF", obj, 4) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_FIF",
+ obj, ACPI_TYPE_INTEGER) != FWTS_OK) {
+ fwts_advice(fw,
+ "%s is not returning the correct "
+ "fan information. It may be worth "
+ "running the firmware test suite "
+ "interactive 'fan' test to see if "
+ "this affects the control and "
+ "operation of the fan.", name);
+ return;
+ }
+
+ fwts_acpi_object_dump(fw, obj);
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_FIF(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FIF", NULL, 0, method_test_FIF_return, NULL);
+}
+
+static void method_test_FPS_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (obj->Package.Elements[0].Type == ACPI_TYPE_INTEGER) {
+ if (obj->Package.Elements[0].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FPSBadRevision",
+ "%s element 0 is not revision 0.", name);
+ failed = true;
+ }
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FPSBadReturnType",
+ "%s element 0 is not an integer.", name);
+ failed = true;
+ }
+
+ /* Could be one or more sub-packages */
+ for (i = 1; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FPSBadReturnType",
+ "%s element %" PRIu32 " is not a package.",
+ name, i);
+ failed = true;
+ continue;
+ }
+
+ pkg = &obj->Package.Elements[i];
+ if (pkg->Package.Count != 5) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FPSBadSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 5 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ for (j = 0; j < 5; j++) {
+ /* TODO - field 0 and 1 can be related to other control method */
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_FPSBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_FPS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FPS", NULL, 0, method_test_FPS_return, NULL);
+}
+
+static int method_test_FSL(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 50;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FSL", arg, 1, method_test_NULL_return, NULL);
+}
+
+static void method_test_FST_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_FST", obj, 3) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_FST",
+ obj, ACPI_TYPE_INTEGER) != FWTS_OK) {
+ fwts_advice(fw,
+ "%s is not returning the correct "
+ "fan status information. It may be "
+ "worth running the firmware test "
+ "suite interactive 'fan' test to see "
+ "if this affects the control and "
+ "operation of the fan.", name);
+ return;
+ }
+
+ fwts_acpi_object_dump(fw, obj);
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_FST(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_FST", NULL, 0, method_test_FST_return, NULL);
+}
+
+
+/*
+ * Section 11.4 Thermal
+ */
+static void method_test_THERM_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ char *method = (char*)private;
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (fwts_acpi_region_handler_called_get()) {
+ /*
+ * We accessed some memory or I/O region during the
+ * evaluation which returns spoofed values, so we
+ * should not test the value being returned. In this
+ * case, just pass this as a valid return type.
+ */
+ method_passed_sane(fw, name, "return type");
+ } else {
+ /*
+ * The evaluation probably was a hard-coded value,
+ * so sanity check it
+ */
+ if (obj->Integer.Value >= 2732) {
+ fwts_passed(fw,
+ "%s correctly returned sane looking "
+ "value 0x%8.8" PRIx64 " (%5.1f degrees K)",
+ method,
+ (uint64_t)obj->Integer.Value,
+ (float)((uint64_t)obj->Integer.Value) / 10.0);
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "MethodBadTemp",
+ "%s returned a dubious value below "
+ "0 degrees C: 0x%8.8" PRIx64 " (%5.1f "
+ "degrees K)",
+ method,
+ (uint64_t)obj->Integer.Value,
+ (float)((uint64_t)obj->Integer.Value) / 10.0);
+ fwts_advice(fw,
+ "The value returned was probably a "
+ "hard-coded thermal value which is "
+ "out of range because fwts did not "
+ "detect any ACPI region handler "
+ "accesses of I/O or system memeory "
+ "to evaluate the thermal value. "
+ "It is worth sanity checking these "
+ "values in "
+ "/sys/class/thermal/thermal_zone*.");
+ }
+ }
+}
+
+#define method_test_THERM(name, type) \
+static int method_test ## name(fwts_framework *fw) \
+{ \
+ fwts_acpi_region_handler_called_set(false); \
+ return method_evaluate_method(fw, type, # name, \
+ NULL, 0, method_test_THERM_return, # name); \
+}
+
+method_test_THERM(_CRT, METHOD_OPTIONAL)
+method_test_THERM(_CR3, METHOD_OPTIONAL)
+method_test_THERM(_HOT, METHOD_OPTIONAL)
+method_test_THERM(_TMP, METHOD_OPTIONAL)
+method_test_THERM(_NTT, METHOD_OPTIONAL)
+method_test_THERM(_PSV, METHOD_OPTIONAL)
+method_test_THERM(_TST, METHOD_OPTIONAL)
+
+static void method_test_MTL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint64_t val;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ val = (uint64_t) obj->Integer.Value;
+ if (val > 100) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_MTLBadReturnType",
+ "%s should return a percentage, got %" PRIu64 " instead", name, val);
+ }
+
+ if (!failed)
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+
+ return;
+}
+
+static int method_test_MTL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_MTL", NULL, 0, method_test_MTL_return, NULL);
+}
+
+static void method_test_ART_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (obj->Package.Elements[0].Type == ACPI_TYPE_INTEGER) {
+ if (obj->Package.Elements[0].Integer.Value != 0) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadRevision",
+ "%s element 0 is not revision 0.", name);
+ failed = true;
+ }
+ } else {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadReturnType",
+ "%s element 0 is not an integer.", name);
+ failed = true;
+ }
+
+ /* Could be one or more sub-packages */
+ for (i = 1; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadReturnType",
+ "%s element %" PRIu32 " is not a package.",
+ name, i);
+ failed = true;
+ continue;
+ }
+
+ pkg = &obj->Package.Elements[i];
+ if (pkg->Package.Count != 13) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 13 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ /* First two elements are references, and rests are integers */
+ for (j = 0; j < 2; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "a reference.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ for (j = 2; j < 13; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_ARTBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_ART(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ART", NULL, 0, method_test_ART_return, "_ART");
+}
+
+static void method_test_PSL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_PSL", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_PSL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_PSL", NULL, 0, method_test_PSL_return, "_PSL");
+}
+
+static void method_test_TRT_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_TRT", obj, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ /* Could be one or more packages */
+ for (i = 0; i < obj->Package.Count; i++) {
+ ACPI_OBJECT *pkg;
+ uint32_t j;
+ bool elements_ok = true;
+
+ pkg = &obj->Package.Elements[i];
+ if (pkg->Package.Count != 8) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TRTSubPackageElementCount",
+ "%s sub-package %" PRIu32 " was expected to "
+ "have 8 elements, got %" PRIu32 " elements instead.",
+ name, i, pkg->Package.Count);
+ failed = true;
+ continue;
+ }
+
+ /* First two elements are references, and rests are integers */
+ for (j = 0; j < 2; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TRTBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "a reference.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ for (j = 2; j < 8; j++) {
+ if (pkg->Package.Elements[j].Type != ACPI_TYPE_INTEGER) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_TRTBadSubPackageReturnType",
+ "%s sub-package %" PRIu32
+ " element %" PRIu32 " is not "
+ "an integer.",
+ name, i, j);
+ elements_ok = false;
+ }
+ }
+
+ if (!elements_ok) {
+ failed = true;
+ continue;
+ }
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_TRT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TRT", NULL, 0, method_test_TRT_return, "_TRT");
+}
+
+static int method_test_TSN(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TSN", NULL, 0, method_test_reference_return, "_TSN");
+}
+
+static int method_test_TSP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TSP", NULL, 0, method_test_polling_return, "_TSP");
+}
+
+static void method_test_TCx_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK)
+ method_passed_sane_uint64(fw, (char*)private, obj->Integer.Value);
+}
+
+static int method_test_TC1(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TC1", NULL, 0, method_test_TCx_return, "_TC1");
+}
+
+static int method_test_TC2(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TC2", NULL, 0, method_test_TCx_return, "_TC1");
+}
+
+static int method_test_TFP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TFP", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_ACx(fwts_framework *fw)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ char buffer[5];
+
+ snprintf(buffer, sizeof(buffer), "_AC%d", i);
+ method_evaluate_method(fw, METHOD_OPTIONAL,
+ buffer, NULL, 0, method_test_THERM_return, buffer);
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+static int method_test_DTI(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 2732 + 800; /* 80 degrees C */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DTI", arg, 1, method_test_NULL_return, NULL);
+}
+
+static int method_test_SCP(fwts_framework *fw)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ACPI_OBJECT arg[3];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i; /* Mode */
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 5; /* Acoustic limit */
+ arg[2].Type = ACPI_TYPE_INTEGER;
+ arg[2].Integer.Value = 5; /* Power limit */
+
+ if (method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DTI", arg, 1, method_test_NULL_return,
+ NULL) == FWTS_NOT_EXIST)
+ break;
+ fwts_log_nl(fw);
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i; /* Mode */
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 1; /* Acoustic limit */
+ arg[2].Type = ACPI_TYPE_INTEGER;
+ arg[2].Integer.Value = 1; /* Power limit */
+
+ if (method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DTI", arg, 1, method_test_NULL_return,
+ NULL) == FWTS_NOT_EXIST)
+ break;
+ }
+ return FWTS_OK;
+}
+
+static void method_test_RTV_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_INTEGER) == FWTS_OK)
+ method_passed_sane_uint64(fw, name, obj->Integer.Value);
+}
+
+static int method_test_RTV(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_RTV", NULL, 0, method_test_RTV_return, "_RTV");
+}
+
+static int method_test_TPT(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 2732 + 900; /* 90 degrees C */
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TPT", arg, 1, method_test_NULL_return, NULL);
+}
+
+static void method_test_TZD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_TZD", obj, ACPI_TYPE_LOCAL_REFERENCE) != FWTS_OK)
+ return;
+
+ fwts_passed(fw, "%s returned a sane package of %" PRIu32 " references.", name, obj->Package.Count);
+}
+
+static int method_test_TZD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TZD", NULL, 0, method_test_TZD_return, "_TZD");
+}
+
+static int method_test_TZM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TZM", NULL, 0, method_test_reference_return, "_TZM");
+}
+
+static int method_test_TZP(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_TZP", NULL, 0, method_test_polling_return, "_TZP");
+}
+
+/*
+ * Section 12 Embedded Controller
+ */
+
+static void method_test_GPE_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+ FWTS_UNUSED(buf);
+ bool failed = false;
+
+ switch (obj->Type) {
+ case ACPI_TYPE_INTEGER:
+ if (obj->Integer.Value <= 255)
+ fwts_passed(fw, "%s returned an integer 0x%8.8" PRIx64,
+ name, (uint64_t)obj->Integer.Value);
+ else
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "MethodGPEInvalidInteger",
+ "%s returned an invalid integer 0x%8.8" PRIx64,
+ name, (uint64_t)obj->Integer.Value);
+ break;
+ case ACPI_TYPE_PACKAGE:
+ if (obj->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_GPEBadSubPackageReturnType",
+ "%s sub-package element 0 is not a reference.", name);
+ }
+
+ if (obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
+ failed = true;
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_GPEBadSubPackageReturnType",
+ "%s sub-package element 1 is not an integer.", name);
+ }
+
+ if (!failed)
+ method_passed_sane(fw, name, "package");
+
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_HIGH, "Method_GPEBadSubReturnType",
+ "%s did not return an integer or a package.", name);
+ break;
+ }
+}
+
+static int method_test_GPE(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GPE", NULL, 0, method_test_GPE_return, "_GPE");
+}
+
+static int method_test_EC_(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_EC_", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Section 16 Waking and Sleeping
+ */
+static int method_test_PTS(fwts_framework *fw)
+{
+ int i;
+
+ for (i = 1; i < 6; i++) {
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i;
+
+ fwts_log_info(fw, "Test _PTS(%d).", i);
+
+ if (method_evaluate_method(fw, METHOD_MANDATORY, "_PTS", arg, 1,
+ method_test_NULL_return, NULL) == FWTS_NOT_EXIST) {
+ fwts_advice(fw,
+ "Could not find _PTS. This method provides a "
+ "mechanism to do housekeeping functions, such "
+ "as write sleep state to the embedded "
+ "controller before entering a sleep state. If "
+ "the machine cannot suspend (S3), "
+ "hibernate (S4) or shutdown (S5) then it "
+ "could be because _PTS is missing. Note that "
+ "ACPI 1.0 wants _PTS to be executed before "
+ "suspending devices.");
+ break;
+ }
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+static int method_test_TTS(fwts_framework *fw)
+{
+ if (fwts_acpi_object_exists("_TTS") != NULL) {
+ int i;
+
+ for (i = 1; i < 6; i++) {
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i;
+
+ fwts_log_info(fw,
+ "Test _TTS(%d) Transition To State S%d.", i, i);
+
+ if (method_evaluate_method(fw, METHOD_MANDATORY,
+ "_TTS", arg, 1, method_test_NULL_return,
+ NULL) == FWTS_NOT_EXIST) {
+ fwts_advice(fw,
+ "Could not find _TTS. This method is invoked "
+ "at the beginning of the the sleep transition "
+ "for S1, S2, S3, S4 and S5 shutdown. The Linux "
+ "kernel caters for firmware that does not implement "
+ "_TTS, however, it will issue a warning that this "
+ "control method is missing.");
+ break;
+ }
+ fwts_log_nl(fw);
+ }
+ }
+ else {
+ fwts_skipped(fw,
+ "Optional control method _TTS does not exist.");
+ }
+ return FWTS_OK;
+}
+
+static void method_test_WAK_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_equal(fw, name, "_WAK", obj, 2) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_WAK", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_WAK(fwts_framework *fw)
+{
+ uint32_t i;
+
+ for (i = 1; i < 6; i++) {
+ ACPI_OBJECT arg[1];
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i;
+ fwts_log_info(fw, "Test _WAK(%d) System Wake, State S%d.", i, i);
+ if (method_evaluate_method(fw, METHOD_MANDATORY, "_WAK", arg, 1,
+ method_test_WAK_return, &i) == FWTS_NOT_EXIST) {
+ fwts_advice(fw,
+ "Section 7.3.7 states that a system that wakes "
+ "from a sleeping state will invoke the _WAK "
+ "control to issue device, thermal and other "
+ "notifications to ensure that the operating system "
+ "checks the states of various devices, thermal "
+ "zones, etc. The Linux kernel will report an "
+ "ACPI exception if _WAK is does not exist when "
+ "it returns from a sleep state.");
+ break;
+ }
+ fwts_log_nl(fw);
+ }
+ return FWTS_OK;
+}
+
+
+/*
+ * Appendix B ACPI Extensions for Display Adapters
+ */
+static int method_test_DOS(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 0 << 2 | 1;
+
+ /*
+ * BIOS should toggle active display, BIOS controls brightness of
+ * LCD on AC/DC power changes
+ */
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DOS", arg, 1, method_test_NULL_return, NULL);
+}
+
+static void method_test_DOD_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+
+ static char *dod_type[] = {
+ "Other",
+ "VGA, CRT or VESA Compatible Analog Monitor",
+ "TV/HDTV or other Analog-Video Monitor",
+ "External Digital Monitor",
+
+ "Internal/Integrated Digital Flat Panel",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved"
+ };
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ for (i = 0; i < obj->Package.Count; i++) {
+ if (obj->Package.Elements[i].Type != ACPI_TYPE_INTEGER)
+ failed = true;
+ else {
+ uint32_t val = obj->Package.Elements[i].Integer.Value;
+ fwts_log_info_verbatim(fw, "Device %" PRIu32 ":", i);
+ if ((val & 0x80000000)) {
+ fwts_log_info_verbatim(fw, " Video Chip Vendor Scheme %" PRIu32, val);
+ } else {
+ fwts_log_info_verbatim(fw, " Instance: %" PRIu32, val & 0xf);
+ fwts_log_info_verbatim(fw, " Display port attachment: %" PRIu32, (val >> 4) & 0xf);
+ fwts_log_info_verbatim(fw, " Type of display: %" PRIu32 " (%s)",
+ (val >> 8) & 0xf, dod_type[(val >> 8) & 0xf]);
+ fwts_log_info_verbatim(fw, " BIOS can detect device: %" PRIu32, (val >> 16) & 1);
+ fwts_log_info_verbatim(fw, " Non-VGA device: %" PRIu32, (val >> 17) & 1);
+ fwts_log_info_verbatim(fw, " Head or pipe ID: %" PRIu32, (val >> 18) & 0x7);
+ }
+ }
+ }
+
+ if (failed)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_DODNoPackage",
+ "Method _DOD did not return a package of "
+ "%" PRIu32 " integers.", obj->Package.Count);
+ else
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_DOD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DOD", NULL, 0, method_test_DOD_return, NULL);
+}
+
+static void method_test_ROM_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ FWTS_UNUSED(obj);
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_BUFFER) == FWTS_OK)
+ method_passed_sane(fw, name, "package");
+}
+
+static int method_test_ROM(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[2];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 0;
+ arg[1].Type = ACPI_TYPE_INTEGER;
+ arg[1].Integer.Value = 4096;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ROM", arg, 2, method_test_ROM_return, NULL);
+}
+
+static int method_test_GPD(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_GPD", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_SPD(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[2];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = i; /* bits 00..11, post device */
+
+ if (method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SPD", arg, 1, method_test_passed_failed_return, NULL) == FWTS_NOT_EXIST)
+ break;
+ }
+ return FWTS_OK;
+}
+
+static int method_test_VPO(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_VPO", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_ADR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_ADR", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_BCL_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t i;
+ bool failed = false;
+ bool ascending_levels = false;
+ char *str = NULL;
+
+ FWTS_UNUSED(private);
+
+ if (method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
+ return;
+
+ if (method_package_count_min(fw, name, "_BCL", obj, 3) != FWTS_OK)
+ return;
+
+ if (method_package_elements_all_type(fw, name, "_BCL", obj, ACPI_TYPE_INTEGER) != FWTS_OK)
+ return;
+
+ if (obj->Package.Elements[0].Integer.Value <
+ obj->Package.Elements[1].Integer.Value) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BCLMaxLevel",
+ "Brightness level when on full "
+ " power (%" PRIu64 ") is less than "
+ "brightness level when on "
+ "battery power (%" PRIu64 ").",
+ (uint64_t)obj->Package.Elements[0].Integer.Value,
+ (uint64_t)obj->Package.Elements[1].Integer.Value);
+ failed = true;
+ }
+
+ for (i = 2; i < obj->Package.Count - 1; i++) {
+ if (obj->Package.Elements[i].Integer.Value >
+ obj->Package.Elements[i+1].Integer.Value) {
+ fwts_log_info(fw,
+ "Brightness level %" PRIu64
+ " (index %" PRIu32 ") is greater "
+ "than brightness level %" PRIu64
+ " (index %d" PRIu32 "), should "
+ "be in ascending order.",
+ (uint64_t)obj->Package.Elements[i].Integer.Value, i,
+ (uint64_t)obj->Package.Elements[i+1].Integer.Value, i+1);
+ ascending_levels = true;
+ failed = true;
+ }
+ }
+ if (ascending_levels) {
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_BCLAscendingOrder",
+ "Some or all of the brightness "
+ "level are not in ascending "
+ "order which should be fixed "
+ "in the firmware.");
+ failed = true;
+ }
+
+ fwts_log_info(fw, "Brightness levels for %s:" ,name);
+ fwts_log_info_verbatim(fw, " Level on full power : %" PRIu64,
+ (uint64_t)obj->Package.Elements[0].Integer.Value);
+ fwts_log_info_verbatim(fw, " Level on battery power: %" PRIu64,
+ (uint64_t)obj->Package.Elements[1].Integer.Value);
+ for (i = 2; i < obj->Package.Count; i++) {
+ char tmp[12];
+
+ snprintf(tmp, sizeof(tmp), "%s%" PRIu64,
+ i == 2 ? "" : ", ",
+ (uint64_t)obj->Package.Elements[i].Integer.Value);
+ str = fwts_realloc_strcat(str, tmp);
+ if (!str)
+ break;
+ }
+ if (str) {
+ fwts_log_info_verbatim(fw, " Brightness Levels : %s", str);
+ free(str);
+ }
+
+ if (failed)
+ fwts_advice(fw,
+ "%s seems to be "
+ "misconfigured and is "
+ "returning incorrect "
+ "brightness levels."
+ "It is worth sanity checking "
+ "this with the firmware test "
+ "suite interactive test "
+ "'brightness' to see how "
+ "broken this is. As it is, "
+ "_BCL is broken and needs to "
+ "be fixed.", name);
+ else
+ fwts_passed(fw,
+ "%s returned a sane "
+ "package of %" PRIu32 " integers.",
+ name, obj->Package.Count);
+}
+
+static int method_test_BCL(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_BCL", NULL, 0, method_test_BCL_return, NULL);
+}
+
+static int method_test_BCM(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 0;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_BCM", arg, 1, method_test_NULL_return, NULL);
+}
+
+static int method_test_BQC(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_BQC", NULL, 0, method_test_integer_return, NULL);
+}
+
+static void method_test_DDC_return(
+ fwts_framework *fw,
+ char *name,
+ ACPI_BUFFER *buf,
+ ACPI_OBJECT *obj,
+ void *private)
+{
+ uint32_t requested = *(uint32_t*)private;
+
+ FWTS_UNUSED(buf);
+
+ if (obj == NULL) {
+ method_failed_null_object(fw, name, "a buffer or integer");
+ return;
+ }
+
+ switch (obj->Type) {
+ case ACPI_TYPE_BUFFER:
+ if (requested != obj->Buffer.Length)
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "Method_DDCElementCount",
+ "%s returned a buffer of %" PRIu32 " items, "
+ "expected %" PRIu32 ".",
+ name, obj->Buffer.Length, requested);
+ else
+ fwts_passed(fw,
+ "Method %s returned a buffer of %d items "
+ "as expected.",
+ name, obj->Buffer.Length);
+ break;
+ case ACPI_TYPE_INTEGER:
+ fwts_passed(fw,
+ "%s could not return a buffer of %d "
+ "items and instead returned an error "
+ "status.",
+ name, obj->Buffer.Length);
+ break;
+ default:
+ fwts_failed(fw, LOG_LEVEL_MEDIUM, "Method_DDCBadReturnType",
+ "%s did not return a buffer or an integer.", name);
+ break;
+ }
+}
+
+static int method_test_DDC(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ uint32_t i;
+
+ for (i = 128; i <= 256; i <<= 1) {
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 128;
+
+ if (method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DDC", arg, 1, method_test_DDC_return,
+ &i) == FWTS_NOT_EXIST)
+ break;
+ }
+ return FWTS_OK;
+}
+
+static int method_test_DCS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DCS", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_DGS(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DGS", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_DSS(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ arg[0].Integer.Value = 0;
+
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_DSS", arg, 1, method_test_NULL_return, NULL);
+}
+
+static int method_test_CBA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CBA", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_CDM(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_CDM", NULL, 0, method_test_integer_return, NULL);
+}
+
+/*
+ * Intelligent Platform Management Interface (IPMI) Specification
+ */
+static int method_test_IFT(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_IFT", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int method_test_SRV(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_OPTIONAL,
+ "_SRV", NULL, 0, method_test_integer_return, NULL);
+}
+
+/* ARM SBBR Test Definitions */
+static int sbbr_method_test_ADR(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_ADR", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int sbbr_method_test_AEI(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_AEI", NULL, 0, method_test_AEI_return, NULL);
+}
+
+static int sbbr_method_test_CCA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_CCA", NULL, 0, method_test_integer_return, NULL);
+}
+
+static int sbbr_method_test_EVT(fwts_framework *fw)
+{
+ int ret;
+
+ /* Only test the _EVT method with pins defined in AEI. */
+ ret = method_evaluate_method(fw, METHOD_MANDATORY,
+ "_AEI", NULL, 0, method_test_EVT_return, NULL);
+
+ if (ret == FWTS_NOT_EXIST)
+ fwts_failed(fw, LOG_LEVEL_HIGH, "SbbrAcpiEvtDoesNotExist", "Method _EVT does not exist.");
+
+ return ret;
+}
+
+static int sbbr_method_test_HID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_HID", NULL, 0, method_test_HID_return, NULL);
+}
+
+static int sbbr_method_test_OSI(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+
+ arg[0].Type = ACPI_TYPE_STRING;
+ arg[0].String.Pointer = "Linux";
+
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_OSI", arg, 1, method_test_integer_return, NULL);
+}
+
+static int sbbr_method_test_SST(fwts_framework *fw)
+{
+ ACPI_OBJECT arg[1];
+ int ret, i;
+
+ arg[0].Type = ACPI_TYPE_INTEGER;
+ for (i = 0; i <= 4; i++) {
+ arg[0].Integer.Value = i;
+ ret = method_evaluate_method(fw, METHOD_MANDATORY,
+ "_SST", arg, 1, method_test_NULL_return, NULL);
+
+ if (ret != FWTS_OK)
+ break;
+ }
+ return ret;
+}
+
+static int sbbr_method_test_STA(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_STA", NULL, 0, method_test_STA_return, "_STA");
+}
+
+static int sbbr_method_test_UID(fwts_framework *fw)
+{
+ return method_evaluate_method(fw, METHOD_MANDATORY,
+ "_UID", NULL, 0, method_test_UID_return, NULL);
+}
+
+
+/*
+ * Tests
+ */
+static fwts_framework_minor_test sbbr_method_tests[] = {
+ { method_name_check, "Test Method Names." },
+
+ /* Section 5.3 */
+ /* { method_test_PR , "Test _PR (Processor)." }, */
+
+ /* Section 5.6 ACPI Event Programming Model */
+ /* { method_test_Wxx, "Test _Wxx (Wake Event)." }, */
+
+ { method_test_AEI, "Test _AEI." },
+ { method_test_EVT, "Test _EVT (Event Method)." },
+
+ /* Section 5.7 Predefined Objects */
+ { method_test_DLM, "Test _DLM (Device Lock Mutex)." },
+ /* { method_test_GL , "Test _GL (Global Lock)." }, */
+ /* { method_test_OS , "Test _OS (Operating System)." }, */
+ /* { method_test_REV, "Test _REV (Revision)." }, */
+
+ /* Section 5.8 System Configuration Objects */
+ { method_test_PIC, "Test _PIC (Inform AML of Interrupt Model)." },
+
+ /* Section 6.1 Device Identification Objects */
+
+ { method_test_CID, "Test _CID (Compatible ID)." },
+ /* { method_test_CLS, "Test _CLS (Class Code)." }, */
+ { method_test_DDN, "Test _DDN (DOS Device Name)." },
+ { method_test_HID, "Test _HID (Hardware ID)." },
+ { method_test_HRV, "Test _HRV (Hardware Revision Number)." },
+ { method_test_MLS, "Test _MLS (Multiple Language String)." },
+ { method_test_PLD, "Test _PLD (Physical Device Location)." },
+ { method_test_SUB, "Test _SUB (Subsystem ID)." },
+ { method_test_SUN, "Test _SUN (Slot User Number)." },
+ { method_test_STR, "Test _STR (String)." },
+ { method_test_UID, "Test _UID (Unique ID)." },
+
+ /* Section 6.2 Device Configurations Objects */
+
+ { method_test_CDM, "Test _CDM (Clock Domain)." },
+ { method_test_CRS, "Test _CRS (Current Resource Settings)." },
+ { method_test_DSD, "Test _DSD (Device Specific Data)." },
+ { method_test_DIS, "Test _DIS (Disable)." },
+ { method_test_DMA, "Test _DMA (Direct Memory Access)." },
+ { method_test_FIX, "Test _FIX (Fixed Register Resource Provider)." },
+ { method_test_GSB, "Test _GSB (Global System Interrupt Base)." },
+ { method_test_HPP, "Test _HPP (Hot Plug Parameters)." },
+ /* { method_test_HPX, "Test _HPX (Hot Plug Extensions)." }, */
+ /* { method_test_MAT, "Test _MAT (Multiple APIC Table Entry)." }, */
+ { method_test_PRS, "Test _PRS (Possible Resource Settings)." },
+ { method_test_PRT, "Test _PRT (PCI Routing Table)." },
+ { method_test_PXM, "Test _PXM (Proximity)." },
+ /* { method_test_SLI, "Test _SLI (System Locality Information)." }, */
+ /* { method_test_SRS, "Test _SRS (Set Resource Settings)." }, */
+ { method_test_CCA, "Test _CCA (Cache Coherency Attribute)." },
+
+ /* Section 6.3 Device Insertion, Removal and Status Objects */
+
+ { method_test_EDL, "Test _EDL (Eject Device List)." },
+ { method_test_EJD, "Test _EJD (Ejection Dependent Device)." },
+ { method_test_EJ0, "Test _EJ0 (Eject)." },
+ { method_test_EJ1, "Test _EJ1 (Eject)." },
+ { method_test_EJ2, "Test _EJ2 (Eject)." },
+ { method_test_EJ3, "Test _EJ3 (Eject)." },
+ { method_test_EJ4, "Test _EJ4 (Eject)." },
+ { method_test_LCK, "Test _LCK (Lock)." },
+ /* { method_test_OST, "Test _OST (OSPM Status Indication)." }, */
+ { method_test_RMV, "Test _RMV (Remove)." },
+ { method_test_STA, "Test _STA (Status)." },
+
+ /* Section 6.4 Resource Data Types for ACPI */
+
+ /* Section 6.5 Other Objects and Controls */
+
+ { method_test_DEP, "Test _DEP (Operational Region Dependencies)." },
+ { method_test_FIT, "Test _FIT (Firmware Interface Table)." },
+ { method_test_BDN, "Test _BDN (BIOS Dock Name)." },
+ { method_test_BBN, "Test _BBN (Base Bus Number)." },
+ { method_test_DCK, "Test _DCK (Dock)." },
+ { method_test_INI, "Test _INI (Initialize)." },
+ { method_test_GLK, "Test _GLK (Global Lock)." },
+ /* { method_test_REG, "Test _REG (Region)." }, */
+ { method_test_SEG, "Test _SEG (Segment)." },
+
+ /* Section 7.1 Declaring a Power Resource Object */
+
+ { method_test_OFF, "Test _OFF (Set resource off)." },
+ { method_test_ON_, "Test _ON_ (Set resource on)." },
+
+ /* Section 7.2 Device Power Management Objects */
+
+ { method_test_DSW, "Test _DSW (Device Sleep Wake)." },
+ { method_test_IRC, "Test _IRC (In Rush Current)." },
+ { method_test_PRE, "Test _PRE (Power Resources for Enumeration)." },
+ { method_test_PR0, "Test _PR0 (Power Resources for D0)." },
+ { method_test_PR1, "Test _PR1 (Power Resources for D1)." },
+ { method_test_PR2, "Test _PR2 (Power Resources for D2)." },
+ { method_test_PR3, "Test _PR3 (Power Resources for D3)." },
+ { method_test_PRW, "Test _PRW (Power Resources for Wake)." },
+ { method_test_PS0, "Test _PS0 (Power State 0)." },
+ { method_test_PS1, "Test _PS1 (Power State 1)." },
+ { method_test_PS2, "Test _PS2 (Power State 2)." },
+ { method_test_PS3, "Test _PS3 (Power State 3)." },
+ { method_test_PSC, "Test _PSC (Power State Current)." },
+ { method_test_PSE, "Test _PSE (Power State for Enumeration)." },
+ { method_test_PSW, "Test _PSW (Power State Wake)." },
+ { method_test_S1D, "Test _S1D (S1 Device State)." },
+ { method_test_S2D, "Test _S2D (S2 Device State)." },
+ { method_test_S3D, "Test _S3D (S3 Device State)." },
+ { method_test_S4D, "Test _S4D (S4 Device State)." },
+ { method_test_S0W, "Test _S0W (S0 Device Wake State)." },
+ { method_test_S1W, "Test _S1W (S1 Device Wake State)." },
+ { method_test_S2W, "Test _S2W (S2 Device Wake State)." },
+ { method_test_S3W, "Test _S3W (S3 Device Wake State)." },
+ { method_test_S4W, "Test _S4W (S4 Device Wake State)." },
+ { method_test_RST, "Test _RST (Device Reset)." },
+ { method_test_PRR, "Test _PRR (Power Resource for Reset)." },
+
+ /* Section 7.3 OEM-Supplied System-Level Control Methods */
+ { method_test_S0_, "Test _S0_ (S0 System State)." },
+ { method_test_S1_, "Test _S1_ (S1 System State)." },
+ { method_test_S2_, "Test _S2_ (S2 System State)." },
+ { method_test_S3_, "Test _S3_ (S3 System State)." },
+ { method_test_S4_, "Test _S4_ (S4 System State)." },
+ { method_test_S5_, "Test _S5_ (S5 System State)." },
+ { method_test_SWS, "Test _SWS (System Wake Source)." },
+
+ /* Section 8.4 Declaring Processors */
+
+ { method_test_PSS, "Test _PSS (Performance Supported States)." },
+ { method_test_CPC, "Test _CPC (Continuous Performance Control)." },
+ { method_test_CSD, "Test _CSD (C State Dependencies)." },
+ { method_test_CST, "Test _CST (C States)." },
+ { method_test_PCT, "Test _PCT (Performance Control)." },
+ /* { method_test_PDC, "Test _PDC (Processor Driver Capabilities)." }, */
+ { method_test_PDL, "Test _PDL (P-State Depth Limit)." },
+ { method_test_PPC, "Test _PPC (Performance Present Capabilities)." },
+ { method_test_PPE, "Test _PPE (Polling for Platform Error)." },
+ { method_test_PSD, "Test _PSD (Power State Dependencies)." },
+ { method_test_PTC, "Test _PTC (Processor Throttling Control)." },
+ { method_test_TDL, "Test _TDL (T-State Depth Limit)." },
+ { method_test_TPC, "Test _TPC (Throttling Present Capabilities)." },
+ { method_test_TSD, "Test _TSD (Throttling State Dependencies)." },
+ { method_test_TSS, "Test _TSS (Throttling Supported States)." },
+
+ /* Section 8.4.4 Lower Power Idle States */
+ { method_test_LPI, "Test _LPI (Low Power Idle States)." },
+ { method_test_RDI, "Test _RDI (Resource Dependencies for Idle)." },
+
+ /* Section 8.5 Processor Aggregator Device */
+ { method_test_PUR, "Test _PUR (Processor Utilization Request)." },
+
+ /* Section 9.1 System Indicators */
+ { method_test_MSG, "Test _MSG (Message)." },
+ { method_test_SST, "Test _SST (System Status)." },
+
+ /* Section 9.2 Ambient Light Sensor Device */
+
+ { method_test_ALC, "Test _ALC (Ambient Light Colour Chromaticity)." },
+ { method_test_ALI, "Test _ALI (Ambient Light Illuminance)." },
+ { method_test_ALT, "Test _ALT (Ambient Light Temperature)." },
+ { method_test_ALP, "Test _ALP (Ambient Light Polling)."},
+ { method_test_ALR, "Test _ALR (Ambient Light Response)."},
+
+ /* Section 9.3 Battery Device */
+
+ /* Section 9.4 Lid Device */
+
+ { method_test_LID, "Test _LID (Lid Status)." },
+
+ /* Section 9.8 ATA Controllers */
+ { method_test_GTF, "Test _GTF (Get Task File)." },
+ { method_test_GTM, "Test _GTM (Get Timing Mode)." },
+ /* { method_test_SDD, "Test _SDD (Set Device Data)." }, */
+ /* { method_test_STM, "Test _STM (Set Timing Mode)." }, */
+
+ /* Section 9.9 Floppy Controllers */
+ /* { method_test_FDE, "Test _FDE (Floppy Disk Enumerate)." }, */
+ /* { method_test_FDI, "Test _FDI (Floppy Drive Information)." }, */
+ /* { method_test_FDM, "Test _FDM (Floppy Drive Mode)." }, */
+
+ /* Section 9.12 Memory Devices */
+ { method_test_MBM, "Test _MBM (Memory Bandwidth Monitoring Data)." },
+ /* { method_test_MSM, "Test _MSM (Memory Set Monitoring)." }, */
+
+ /* Section 9.13 USB Port Capabilities */
+ { method_test_UPC, "Test _UPC (USB Port Capabilities)." },
+
+ /* Section 9.14 Device Object Name Collision */
+ /* { method_test_DSM, "Test _DSM (Device Specific Method)." }, */
+
+ /* Section 9.16 User Presence Detection Device */
+ { method_test_UPD, "Test _UPD (User Presence Detect)." },
+ { method_test_UPP, "Test _UPP (User Presence Polling)." },
+
+ /* Section 9.18 Wake Alarm Device */
+
+ { method_test_GCP, "Test _GCP (Get Capabilities)." },
+ { method_test_GRT, "Test _GRT (Get Real Time)." },
+ { method_test_GWS, "Test _GWS (Get Wake Status)." },
+ { method_test_CWS, "Test _CWS (Clear Wake Status)." },
+ { method_test_SRT, "Test _SRT (Set Real Time)." },
+ { method_test_STP, "Test _STP (Set Expired Timer Wake Policy)." },
+ { method_test_STV, "Test _STV (Set Timer Value)." },
+ { method_test_TIP, "Test _TIP (Expired Timer Wake Policy)." },
+ { method_test_TIV, "Test _TIV (Timer Values)." },
+
+ /* Section 10.1 Smart Battery */
+
+ { method_test_SBS, "Test _SBS (Smart Battery Subsystem)." },
+
+ /* Section 10.2 Battery Controls */
+
+ { method_test_BCT, "Test _BCT (Battery Charge Time)." },
+ { method_test_BIF, "Test _BIF (Battery Information)." },
+ { method_test_BIX, "Test _BIX (Battery Information Extended)." },
+ { method_test_BMA, "Test _BMA (Battery Measurement Averaging)." },
+ { method_test_BMC, "Test _BMC (Battery Maintenance Control)." },
+ { method_test_BMD, "Test _BMD (Battery Maintenance Data)." },
+ { method_test_BMS, "Test _BMS (Battery Measurement Sampling Time)." },
+ { method_test_BST, "Test _BST (Battery Status)." },
+ { method_test_BTP, "Test _BTP (Battery Trip Point)." },
+ { method_test_BTH, "Test _BTH (Battery Throttle Limit)." },
+ { method_test_BTM, "Test _BTM (Battery Time)." },
+ /* { method_test_BLT, "Test _BLT (Battery Level Threshold)." }, */
+
+ /* Section 10.3 AC Adapters and Power Source Objects */
+
+ { method_test_PCL, "Test _PCL (Power Consumer List)." },
+ { method_test_PIF, "Test _PIF (Power Source Information)." },
+ { method_test_PRL, "Test _PRL (Power Source Redundancy List)." },
+ { method_test_PSR, "Test _PSR (Power Source)." },
+
+ /* Section 10.4 Power Meters */
+ { method_test_GAI, "Test _GAI (Get Averaging Level)." },
+ { method_test_GHL, "Test _GHL (Get Harware Limit)." },
+ /* { method_test_PAI, "Test _PAI (Power Averaging Interval)." }, */
+ { method_test_PMC, "Test _PMC (Power Meter Capabilities)." },
+ { method_test_PMD, "Test _PMD (Power Meter Devices)." },
+ { method_test_PMM, "Test _PMM (Power Meter Measurement)." },
+ /* { method_test_PTP, "Test _PTP (Power Trip Points)." }, */
+ /* { method_test_SHL, "Test _SHL (Set Hardware Limit)." }, */
+
+ /* Section 10.5 Wireless Power Controllers */
+ { method_test_WPC, "Test _WPC (Wireless Power Calibration)." },
+ { method_test_WPP, "Test _WPP (Wireless Power Polling)." },
+
+ /* Section 11.3 Fan Devices */
+
+ { method_test_FIF, "Test _FIF (Fan Information)." },
+ { method_test_FPS, "Test _FPS (Fan Performance States)." },
+ { method_test_FSL, "Test _FSL (Fan Set Level)." },
+ { method_test_FST, "Test _FST (Fan Status)." },
+
+ /* Section 11.4 Thermal Objects */
+
+ { method_test_ACx, "Test _ACx (Active Cooling)." },
+ { method_test_ART, "Test _ART (Active Cooling Relationship Table)." },
+ /* { method_test_ALx, "Test _ALx (Active List)". }, */
+ { method_test_CRT, "Test _CRT (Critical Trip Point)." },
+ { method_test_CR3, "Test _CR3 (Warm/Standby Temperature)." },
+ { method_test_DTI, "Test _DTI (Device Temperature Indication)." },
+ { method_test_HOT, "Test _HOT (Hot Temperature)." },
+ { method_test_MTL, "Test _MTL (Minimum Throttle Limit)." },
+ { method_test_NTT, "Test _NTT (Notification Temp Threshold)." },
+ { method_test_PSL, "Test _PSL (Passive List)." },
+ { method_test_PSV, "Test _PSV (Passive Temp)." },
+ { method_test_RTV, "Test _RTV (Relative Temp Values)." },
+ { method_test_SCP, "Test _SCP (Set Cooling Policy)." },
+ { method_test_TC1, "Test _TC1 (Thermal Constant 1)." },
+ { method_test_TC2, "Test _TC2 (Thermal Constant 2)." },
+ { method_test_TFP, "Test _TFP (Thermal fast Sampling Period)." },
+ { method_test_TMP, "Test _TMP (Thermal Zone Current Temp)." },
+ { method_test_TPT, "Test _TPT (Trip Point Temperature)." },
+ { method_test_TRT, "Test _TRT (Thermal Relationship Table)." },
+ { method_test_TSN, "Test _TSN (Thermal Sensor Device)." },
+ { method_test_TSP, "Test _TSP (Thermal Sampling Period)." },
+ { method_test_TST, "Test _TST (Temperature Sensor Threshold)." },
+ { method_test_TZD, "Test _TZD (Thermal Zone Devices)." },
+ { method_test_TZM, "Test _TZM (Thermal Zone member)." },
+ { method_test_TZP, "Test _TZP (Thermal Zone Polling)." },
+
+ /* Section 12 Embedded Controller Interface */
+ { method_test_GPE, "Test _GPE (General Purpose Events)." },
+ { method_test_EC_, "Test _EC_ (EC Offset Query)." },
+
+ /* Section 16 Waking and Sleeping */
+
+ { method_test_PTS, "Test _PTS (Prepare to Sleep)." },
+ { method_test_TTS, "Test _TTS (Transition to State)." },
+ { method_test_WAK, "Test _WAK (System Wake)." },
+
+ /* Appendix B, ACPI Extensions for Display Adapters */
+
+ { method_test_ADR, "Test _ADR (Return Unique ID for Device)." },
+ { method_test_BCL, "Test _BCL (Query List of Brightness Control Levels Supported)." },
+ { method_test_BCM, "Test _BCM (Set Brightness Level)." },
+ { method_test_BQC, "Test _BQC (Brightness Query Current Level)." },
+ { method_test_DCS, "Test _DCS (Return the Status of Output Device)." },
+ { method_test_DDC, "Test _DDC (Return the EDID for this Device)." },
+ { method_test_DSS, "Test _DSS (Device Set State)." },
+ { method_test_DGS, "Test _DGS (Query Graphics State)." },
+ { method_test_DOD, "Test _DOD (Enumerate All Devices Attached to Display Adapter)." },
+ { method_test_DOS, "Test _DOS (Enable/Disable Output Switching)." },
+ { method_test_GPD, "Test _GPD (Get POST Device)." },
+ { method_test_ROM, "Test _ROM (Get ROM Data)." },
+ { method_test_SPD, "Test _SPD (Set POST Device)." },
+ { method_test_VPO, "Test _VPO (Video POST Options)." },
+
+ /* From PCI Specification */
+ { method_test_CBA, "Test _CBA (Configuration Base Address)." },
+
+ /* From IPMI Specification 2.0 */
+ { method_test_IFT, "Test _IFT (IPMI Interface Type)." },
+ { method_test_SRV, "Test _SRV (IPMI Interface Revision)." },
+
+ /* From ARM SBBR */
+ { sbbr_method_test_ADR, "SBBR Test _ADR (Return Unique ID for Device)." },
+ { sbbr_method_test_AEI, "SBBR Test _AEI (Event Information)." },
+ { sbbr_method_test_CCA, "SBBR Test _CCA (Cache Coherency Attribute)." },
+ { sbbr_method_test_EVT, "SBBR Test _EVT (Event Method)." },
+ { sbbr_method_test_HID, "SBBR Test _HID (Hardware ID)." },
+ { sbbr_method_test_OSI, "SBBR Test _OSI (Operating System Interfaces)." },
+ { sbbr_method_test_SST, "SBBR Test _SST (System Status)." },
+ { sbbr_method_test_STA, "SBBR Test _STA (Status)." },
+ { sbbr_method_test_UID, "SBBR Test _UID (Unique ID)." },
+
+ /* End! */
+
+ { NULL, NULL }
+};
+
+static fwts_framework_ops sbbr_method_ops = {
+ .description = "ACPI DSDT Method Semantic tests.",
+ .init = sbbr_method_init,
+ .deinit = sbbr_method_deinit,
+ .minor_tests = sbbr_method_tests
+};
+
+FWTS_REGISTER("sbbr_method", &sbbr_method_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
+
+#endif
--
2.7.4
More information about the fwts-devel
mailing list