Message ID | 1488493596-3437-12-git-send-email-supreeth.venkatesh@arm.com |
---|---|
State | Superseded |
Headers | show |
This is duplication of the fwts method test. What's the justification? Cut-n-paste duplication makes maintenance really hard work. On 02/03/17 22:26, Supreeth Venkatesh wrote: > 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@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 >
On Thu, 2017-03-02 at 23:57 +0000, Colin Ian King wrote: > This is duplication of the fwts method test. What's the > justification? > Cut-n-paste duplication makes maintenance really hard work. Please suggest alternate method from maintainability aspect. I could actually use "FWTS_HAS_SBBR" Pre-Compiler Switch to differentiate from the normal flow, but then again it wont be nicely separated and all sbbr test cases will not be in one place. Please suggest and I will make changes as per your feedback. > > On 02/03/17 22:26, Supreeth Venkatesh wrote: > > > > 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.den0044 > > b/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@arm.com> > > --- > >
On 3/6/2017 10:39 AM, Supreeth Venkatesh wrote: > On Thu, 2017-03-02 at 23:57 +0000, Colin Ian King wrote: >> This is duplication of the fwts method test. What's the >> justification? >> Cut-n-paste duplication makes maintenance really hard work. > Please suggest alternate method from maintainability aspect. > I could actually use "FWTS_HAS_SBBR" Pre-Compiler Switch to > differentiate from the normal flow, but then again it wont be nicely > separated and all sbbr test cases will not be in one place. > Please suggest and I will make changes as per your feedback. Is it possible to integrate the SBBR content into the existing tests with a runtime switch so that the existing tests go through the existing, normal flow by default, and the SBBR flow when --sbbr is given on the command line? >> >> On 02/03/17 22:26, Supreeth Venkatesh wrote: >>> >>> 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.den0044 >>> b/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@arm.com> >>> --- >>> >
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
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@arm.com> --- src/sbbr/method/method.c | 7333 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 7333 insertions(+) create mode 100644 src/sbbr/method/method.c