ACK: [PATCH] acpi: wmi: re-write to eval _WDG rather than parse AML

Alex Hung alex.hung at canonical.com
Mon Feb 18 07:18:24 UTC 2013


On 02/13/2013 05:34 PM, Colin King 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
>   };
>
>
Acked-by: Alex Hung <alex.hung at canonical.com>



More information about the fwts-devel mailing list