[PATCH] acpi: wmi: re-write to eval _WDG rather than parse AML
Keng-Yu Lin
kengyu at canonical.com
Tue Feb 19 06:59:04 UTC 2013
On Wed, Feb 13, 2013 at 5:34 PM, Colin King <colin.king at canonical.com> wrote:
> From: Colin Ian King <colin.king at canonical.com>
>
> This patch is a re-write of the acpi WMI test. It no longer
> parses the disassembled AML but instead evaluates all the _WDG
> objects and parses and dumps the associated buffer. This is a
> little slower but should remove all the complexity of parsing
> disassembled code which could break if the output changes in
> the future.
>
> Also, do some sanity checking to see if methods associated
> with WMI events exist or not.
>
> Signed-off-by: Colin Ian King <colin.king at canonical.com>
> ---
> src/acpi/wmi/wmi.c | 512 ++++++++++++++++++++++++++++-------------------------
> 1 file changed, 268 insertions(+), 244 deletions(-)
>
> diff --git a/src/acpi/wmi/wmi.c b/src/acpi/wmi/wmi.c
> index 75b0aa4..6401b41 100644
> --- a/src/acpi/wmi/wmi.c
> +++ b/src/acpi/wmi/wmi.c
> @@ -28,6 +28,10 @@
> #include <string.h>
> #include <ctype.h>
>
> +/* acpica headers */
> +#include "acpi.h"
> +#include "fwts_acpi_object_eval.h"
> +
> typedef enum {
> FWTS_WMI_EXPENSIVE = 0x00000001,
> FWTS_WMI_METHOD = 0x00000002,
> @@ -36,9 +40,14 @@ typedef enum {
> } fwts_wmi_flags;
>
> typedef struct {
> - char *guid;
> - char *driver;
> - char *vendor;
> + const fwts_wmi_flags flags;
> + const char *name;
> +} fwts_wmi_flags_name;
> +
> +typedef struct {
> + const char *guid; /* GUID string */
> + const char *driver; /* Kernel Driver name */
> + const char *vendor; /* Machine Vendor */
> } fwts_wmi_known_guid;
>
> /*
> @@ -50,13 +59,16 @@ typedef struct {
> uint8_t obj_id[2]; /* Object Identifier */
> struct {
> uint8_t notify_id; /* Notify Identifier */
> - uint8_t reserved; /* Reserved? */
> + uint8_t reserved; /* Reserved */
> };
> };
> uint8_t instance; /* Instance */
> uint8_t flags; /* fwts_wmi_flags */
> -} __attribute__ ((packed)) fwts_guid_info;
> +} __attribute__ ((packed)) fwts_wdg_info;
>
> +/*
> + * Bunch of known WMI GUIDs in the kernel
> + */
> static fwts_wmi_known_guid fwts_wmi_known_guids[] = {
> { "67C3371D-95A3-4C37-BB61-DD47B491DAAB", "acer-wmi", "Acer" },
> { "431F16ED-0C2B-444C-B267-27DEB140CF9C", "acer-wmi", "Acer" },
> @@ -77,6 +89,46 @@ static fwts_wmi_known_guid fwts_wmi_known_guids[] = {
> { NULL, NULL, NULL }
> };
>
> +/*
> + * WMI flag to text mappings
> + */
> +static const fwts_wmi_flags_name wmi_flags_name[] = {
> + { FWTS_WMI_EXPENSIVE, "Expensive" },
> + { FWTS_WMI_METHOD, "Method" },
> + { FWTS_WMI_STRING, "String" },
> + { FWTS_WMI_EVENT, "Event" },
> + { 0, NULL }
> +};
> +
> +static bool wmi_advice_given;
> +
> +/*
> + * wmi_init()
> + * initialize ACPI
> + */
> +static int wmi_init(fwts_framework *fw)
> +{
> + if (fwts_acpi_init(fw) != FWTS_OK) {
> + fwts_log_error(fw, "Cannot initialise ACPI.");
> + return FWTS_ERROR;
> + }
> +
> + return FWTS_OK;
> +}
> +
> +/*
> + * wmi_deinit()
> + * de-intialize ACPI
> + */
> +static int wmi_deinit(fwts_framework *fw)
> +{
> + return fwts_acpi_deinit(fw);
> +}
> +
> +/*
> + * fwts_wmi_known_guid()
> + * find any known GUID driver info
> + */
> static fwts_wmi_known_guid *wmi_find_guid(char *guid)
> {
> fwts_wmi_known_guid *info = fwts_wmi_known_guids;
> @@ -88,296 +140,268 @@ static fwts_wmi_known_guid *wmi_find_guid(char *guid)
> return NULL;
> }
>
> -#define CONSUME_WHITESPACE(str) \
> - while (*str && isspace(*str)) \
> - str++; \
> - if (*str == '\0') return; \
> +/*
> + * wmi_strncat()
> + * build up a description of flag settings
> + */
> +static char *wmi_strncat(char *dst, const char *str, const size_t dst_len)
> +{
> + if (*dst)
> + strncat(dst, " | ", dst_len);
> +
> + return strncat(dst, str, dst_len);
> +}
>
> +/*
> + * wmi_wdg_flags_to_text()
> + * turn WDG flags into a description string
> + */
> static char *wmi_wdg_flags_to_text(const fwts_wmi_flags flags)
> {
> - static char buffer[256];
> + static char buffer[1024];
> + int i;
>
> *buffer = 0;
>
> - if (flags & FWTS_WMI_EXPENSIVE)
> - strcat(buffer, "WMI_EXPENSIVE ");
> - if (flags & FWTS_WMI_METHOD)
> - strcat(buffer, "WMI_METHOD");
> - if (flags & FWTS_WMI_STRING)
> - strcat(buffer, "WMI_STRING");
> - if (flags & FWTS_WMI_EVENT)
> - strcat(buffer, "WMI_EVENT ");
> + for (i = 0; wmi_flags_name[i].flags; i++)
> + if (flags & wmi_flags_name[i].flags)
> + wmi_strncat(buffer, wmi_flags_name[i].name, sizeof(buffer) - 1);
> +
> + if (!*buffer)
> + strncpy(buffer, "None", sizeof(buffer) - 1);
>
> return buffer;
> }
>
> -static void wmi_parse_wdg_data(fwts_framework *fw,
> - const size_t size, const uint8_t *wdg_data, bool *result)
> +/*
> + * wmi_method_exist_count()
> + * check if an associated method exists for the WDG object
> + */
> +static void wmi_method_exist_count(
> + fwts_framework *fw,
> + const fwts_wdg_info *info,
> + const char *guid_str)
> {
> - size_t i;
> - int advice_given = 0;
> -
> - fwts_guid_info *info = (fwts_guid_info *)wdg_data;
> -
> - for (i=0; i<(size / sizeof(fwts_guid_info)); i++) {
> - uint8_t *guid = info->guid;
> - char guidstr[37];
> - fwts_wmi_known_guid *known;
> -
> - fwts_guid_buf_to_str(guid, guidstr, sizeof(guidstr));
> -
> - known = wmi_find_guid(guidstr);
> -
> - if (info->flags & FWTS_WMI_METHOD) {
> - fwts_log_info(fw,
> - "Found WMI Method WM%c%c with GUID: %s, "
> - "Instance 0x%2.2x",
> - info->obj_id[0], info->obj_id[1],
> - guidstr, info->instance);
> - } else if (info->flags & FWTS_WMI_EVENT) {
> - fwts_log_info(fw,
> - "Found WMI Event, Notifier ID: 0x%2.2x, "
> - "GUID: %s, Instance 0x%2.2x",
> - info->notify_id, guidstr, info->instance);
> - if (known == NULL) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "WMIUnknownGUID",
> - "GUID %s is unknown to the kernel, "
> - "a driver may need to be implemented "
> - "for this GUID.", guidstr);
> - *result = true;
> - if (!advice_given) {
> - advice_given = 1;
> - fwts_log_nl(fw);
> - fwts_log_advice(fw,
> - "ADVICE: A WMI driver probably "
> - "needs to be written for this "
> - "event.");
> - fwts_log_advice(fw,
> - "It can checked for using: "
> - "wmi_has_guid(\"%s\").",
> - guidstr);
> - fwts_log_advice(fw,
> - "One can install a notify "
> - "handler using "
> - "wmi_install_notify_handler"
> - "(\"%s\", handler, NULL). ",
> - guidstr);
> - fwts_log_advice(fw,
> - "http://lwn.net/Articles/391230"
> - " describes how to write an "
> - "appropriate driver.");
> - fwts_log_nl(fw);
> - }
> - }
> - } else {
> - char *flags = wmi_wdg_flags_to_text(info->flags);
> - fwts_log_info(fw,
> - "Found WMI Object, Object ID %c%c, "
> - "GUID: %s, Instance 0x%2.2x, Flags: %2.2x %s",
> - info->obj_id[0], info->obj_id[1], guidstr,
> - info->instance, info->flags, flags);
> - }
> -
> - if (known) {
> - fwts_passed(fw,
> - "GUID %s is handled by driver %s (Vendor: %s).",
> - guidstr, known->driver, known->vendor);
> - *result = true;
> + fwts_list_link *item;
> + fwts_list *objects;
> + const size_t wm_name_len = 4;
> + char wm_name[5];
> + char *objname = "";
> + int count = 0;
> +
> + snprintf(wm_name, sizeof(wm_name), "WM%c%c",
> + info->obj_id[0], info->obj_id[1]);
> +
> + if ((objects = fwts_acpi_object_get_names()) == NULL)
> + return; /* Should not ever happen, bail out if it does */
> +
> + fwts_list_foreach(item, objects) {
> + char *name = fwts_list_data(char*, item);
> + const size_t name_len = strlen(name);
> + if (strncmp(wm_name, name + name_len - wm_name_len, wm_name_len) == 0) {
> + objname = name;
> + count++;
> }
> -
> - info++;
> }
> +
> + if (count == 0) {
> + fwts_failed(fw, LOG_LEVEL_LOW,
> + "WMIMissingMethod",
> + "GUID %s should have an associated method WM%c%c defined, "
> + "however this does not seem to exist.",
> + guid_str, info->obj_id[0], info->obj_id[1]);
> + } else if (count > 1) {
> + fwts_failed(fw, LOG_LEVEL_LOW,
> + "WMIMultipleMethod",
> + "GUID %s has multiple associated methods WM%c%c defined, "
> + "this is a firmware bug that leads to ambigous behaviour.",
> + guid_str, info->obj_id[0], info->obj_id[1]);
> + } else
> + fwts_passed(fw, "%s has associated method %s", guid_str, objname);
> }
>
> -static void wmi_get_wdg_data(fwts_framework *fw,
> - fwts_list_link *item, const size_t size, uint8_t *wdg_data)
> +/*
> + * wmi_no_known_driver()
> + * grumble that the kernel does not have a known handler for this GUID
> + */
> +static void wmi_no_known_driver(
> + fwts_framework *fw,
> + const char *guid_str)
> {
> - char *str;
> - uint8_t *data = wdg_data;
> -
> - for (;item != NULL; item=item->next) {
> - int i;
> - str = fwts_text_list_text(item);
> - CONSUME_WHITESPACE(str);
> -
> - if (*str == '}') break;
>
> - if (strncmp(str, "/*", 2) == 0) {
> - str = strstr(str, "*/");
> - if (str)
> - str += 2;
> - else
> - continue;
> - }
> - CONSUME_WHITESPACE(str);
> -
> - for (i=0;i<8;i++) {
> - if (strncmp(str, "0x", 2))
> - break;
> - *data = strtoul(str, NULL, 16);
> - str+=4;
> - if (*str != ',') break;
> - str++;
> - if (!isspace(*str)) {
> - data++;
> - break;
> - }
> - str++;
> - data++;
> - if (data > wdg_data + size) {
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - "WMI_WDGBufferBad",
> - "_WDG buffer was more than %zu bytes "
> - "long!", size);
> - fwts_tag_failed(fw, FWTS_TAG_ACPI_BAD_LENGTH);
> - return;
> - }
> - }
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "WMIUnknownGUID",
> + "GUID %s is unknown to the kernel, a driver may need to "
> + "be implemented for this GUID.", guid_str);
> +
> + if (!wmi_advice_given) {
> + wmi_advice_given = true;
> + fwts_log_advice(fw,
> + "A WMI driver probably needs to be written for this "
> + "WMI event. It can checked for using: wmi_has_guid(\"%s\"). "
> + "One can install a notify handler using "
> + "wmi_install_notify_handler(\"%s\", handler, NULL). "
> + "http://lwn.net/Articles/391230 describes how to write an "
> + "appropriate driver.",
> + guid_str, guid_str);
> }
> - return;
> }
>
> -static void wmi_parse_for_wdg(fwts_framework *fw,
> - fwts_list_link *item, int *count, bool *result)
> +/*
> + * wmi_dump_object()
> + * dump out a WDG object
> + */
> +static void wmi_dump_object(fwts_framework *fw, const fwts_wdg_info *info)
> {
> - uint8_t *wdg_data;
> - size_t size;
> - char *str = fwts_text_list_text(item);
> -
> - /* Parse Name(_WDG, Buffer, (0xXX) */
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (strncmp(str, "Name", 4))
> - return;
> - str += 4;
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (*str != '(') return;
> - str++;
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (strncmp(str, "_WDG",4))
> - return;
> - str+=4;
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (*str != ',') return;
> - str++;
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (strncmp(str, "Buffer",6))
> - return;
> - str+=6;
> -
> - CONSUME_WHITESPACE(str);
> -
> - if (*str != '(') return;
> - str++;
> -
> - CONSUME_WHITESPACE(str);
> -
> - size = strtoul(str, NULL, 16);
> -
> - item = item->next;
> - if (item == NULL) return;
> -
> - str = fwts_text_list_text(item);
> - CONSUME_WHITESPACE(str);
> - if (*str != '{') return;
> -
> - item = item->next;
> - if (item == NULL) return;
> + fwts_log_info_verbatum(fw, " Flags : 0x%2.2" PRIx8 " (%s)",
> + info->flags, wmi_wdg_flags_to_text(info->flags));
> + fwts_log_info_verbatum(fw, " Object ID : %c%c",
> + info->obj_id[0], info->obj_id[1]);
> + fwts_log_info_verbatum(fw, " Instance : 0x%2.2" PRIx8,
> + info->instance);
> +}
>
> - if ((wdg_data = calloc(1, size)) != NULL) {
> - (*count)++ ;
> - wmi_get_wdg_data(fw, item, size, wdg_data);
> - wmi_parse_wdg_data(fw, size, wdg_data, result);
> - free(wdg_data);
> +/*
> + * wmi_known_driver()
> + * report info about the supported kernel driver
> + */
> +static void wmi_known_driver(
> + fwts_framework *fw,
> + const fwts_wmi_known_guid *known)
> +{
> + /* If we recognise the GUID then we may as well report this info */
> + if (known) {
> + fwts_log_info_verbatum(fw, " Driver : %s (%s)",
> + known->driver, known->vendor);
> }
> }
>
> -static int wmi_table(fwts_framework *fw,
> - const char *table, const int which, const char *name, bool *result)
> +/*
> + * wmi_parse_wdg_data()
> + * parse over raw _WDG data and dump + sanity check the objects
> + */
> +static void wmi_parse_wdg_data(
> + fwts_framework *fw,
> + const char *name,
> + const size_t size,
> + const uint8_t *wdg_data)
> {
> - fwts_list_link *item;
> - fwts_list* iasl_output;
> - int count = 0;
> - int ret;
> -
> - ret = fwts_iasl_disassemble(fw, table, which, &iasl_output);
> - if (ret == FWTS_NO_TABLE) /* Nothing more to do */
> - return ret;
> - if (ret != FWTS_OK) {
> - fwts_aborted(fw, "Cannot disassemble and parse for "
> - "WMI information.");
> - return FWTS_ERROR;
> - }
> + size_t i;
> + fwts_wdg_info *info = (fwts_wdg_info *)wdg_data;
> + bool all_events_known = true;
> + bool events = false;
>
> - fwts_list_foreach(item, iasl_output)
> - wmi_parse_for_wdg(fw, item, &count, result);
> + for (i = 0; i < (size / sizeof(fwts_wdg_info)); i++, info++) {
> + uint8_t *guid = info->guid;
> + char guid_str[37];
> + const fwts_wmi_known_guid *known;
>
> - if (count == 0)
> - fwts_log_info(fw, "No WMI data found in table %s.", name);
> + fwts_guid_buf_to_str(guid, guid_str, sizeof(guid_str));
> + fwts_log_nl(fw);
> + fwts_log_info_verbatum(fw, "%s (%zd of %zd)",
> + name, i + 1, size / sizeof(fwts_wdg_info));
> + fwts_log_info_verbatum(fw, " GUID: %s", guid_str);
> + known = wmi_find_guid(guid_str);
>
> - fwts_text_list_free(iasl_output);
> + if (info->flags & FWTS_WMI_METHOD) {
> + fwts_log_info_verbatum(fw, " WMI Method:");
> + wmi_dump_object(fw, info);
> + wmi_known_driver(fw, known);
> + wmi_method_exist_count(fw, info, guid_str);
> + } else if (info->flags & FWTS_WMI_EVENT) {
> + events = true;
> + fwts_log_info_verbatum(fw, " WMI Event:");
> + fwts_log_info_verbatum(fw, " Flags : 0x%2.2" PRIx8 " (%s)",
> + info->flags, wmi_wdg_flags_to_text(info->flags));
> + fwts_log_info_verbatum(fw, " Notification ID: 0x%2.2" PRIx8,
> + info->notify_id);
> + fwts_log_info_verbatum(fw, " Reserved : 0x%2.2" PRIx8,
> + info->reserved);
> + fwts_log_info_verbatum(fw, " Instance : 0x%2.2" PRIx8,
> + info->instance);
> + wmi_known_driver(fw, known);
> +
> + /* To handle events we really need a custom kernel driver */
> + if (!known) {
> + wmi_no_known_driver(fw, guid_str);
> + all_events_known = false;
> + }
> + } else {
> + fwts_log_info_verbatum(fw, " WMI Object:");
> + wmi_dump_object(fw, info);
> + wmi_known_driver(fw, known);
> + }
> + }
>
> - return FWTS_OK;
> + if (events && all_events_known)
> + fwts_passed(fw, "All events associated with %s are handled by a kernel driver.", name);
> }
>
> -static int wmi_DSDT(fwts_framework *fw)
> +static int wmi_test1(fwts_framework *fw)
> {
> - bool result = false;
> - int ret;
> + fwts_list_link *item;
> + fwts_list *objects;
> + const size_t name_len = 4;
> + bool wdg_found = false;
>
> - ret = wmi_table(fw, "DSDT", 0, "DSDT", &result);
> + if ((objects = fwts_acpi_object_get_names()) == NULL) {
> + fwts_log_info(fw, "Cannot find any ACPI objects");
> + return FWTS_ERROR;
> + }
>
> - if (!result)
> - fwts_infoonly(fw);
> + wmi_advice_given = false;
>
> - return ret;
> -}
> + fwts_list_foreach(item, objects) {
> + char *name = fwts_list_data(char*, item);
> + const size_t len = strlen(name);
>
> -static int wmi_SSDT(fwts_framework *fw)
> -{
> - int i;
> - bool result = false;
> - int ret = FWTS_OK;
> + if (strncmp("_WDG", name + len - name_len, name_len) == 0) {
> + ACPI_OBJECT_LIST arg_list;
> + ACPI_BUFFER buf;
> + ACPI_OBJECT *obj;
> + int ret;
>
> - for (i=0; i < 16; i++) {
> - char buffer[10];
> - snprintf(buffer, sizeof(buffer), "SSDT%d", i+1);
> + arg_list.Count = 0;
> + arg_list.Pointer = NULL;
>
> - ret = wmi_table(fw, "SSDT", i, buffer, &result);
> - if (ret == FWTS_NO_TABLE) {
> - ret = FWTS_OK; /* Hit the last table */
> - break;
> - }
> - if (ret != FWTS_OK) {
> - ret = FWTS_ERROR;
> - break;
> + ret = fwts_acpi_object_evaluate(fw, name, &arg_list, &buf);
> + if ((ACPI_FAILURE(ret) != AE_OK) || (buf.Pointer == NULL))
> + continue;
> +
> + /* Do we have a valid buffer to dump? */
> + obj = buf.Pointer;
> + if ((obj->Type == ACPI_TYPE_BUFFER) &&
> + (obj->Buffer.Pointer != NULL) &&
> + (obj->Buffer.Length > 0)) {
> + wmi_parse_wdg_data(fw, name,
> + obj->Buffer.Length,
> + (uint8_t*)obj->Buffer.Pointer);
> + wdg_found = true;
> + }
> +
> + if (buf.Length && buf.Pointer)
> + free(buf.Pointer);
> }
> }
> - if (!result)
> - fwts_infoonly(fw);
>
> - return ret;
> + if (!wdg_found) {
> + fwts_log_info(fw, "No ACPI _WDG WMI data found.");
> + return FWTS_SKIP;
> + }
> +
> + return FWTS_OK;
> }
>
> static fwts_framework_minor_test wmi_tests[] = {
> - { wmi_DSDT, "Check Windows Management Instrumentation in DSDT" },
> - { wmi_SSDT, "Check Windows Management Instrumentation in SSDT" },
> + { wmi_test1, "Check Windows Management Instrumentation" },
> { NULL, NULL }
> };
>
> static fwts_framework_ops wmi_ops = {
> .description = "Extract and analyse Windows Management "
> "Instrumentation (WMI).",
> + .init = wmi_init,
> + .deinit = wmi_deinit,
> .minor_tests = wmi_tests
> };
>
> --
> 1.8.1.2
>
Acked-by: Keng-Yu Lin <kengyu at canonical.com>
More information about the fwts-devel
mailing list