diff mbox series

dsddump: add utility to dump _DSDD (Device Specific Data)

Message ID 20190109004418.20455-1-alex.hung@canonical.com
State Accepted
Headers show
Series dsddump: add utility to dump _DSDD (Device Specific Data) | expand

Commit Message

Alex Hung Jan. 9, 2019, 12:44 a.m. UTC
Buglink: https://bugs.launchpad.net/bugs/1433643

In addition to ACPI spec, this patch also refers to two documents:

 - Device Properties UUID For _DSD
 - Hierarchical Data Extension UUID For _DSD

Both documents can be found @ http://uefi.org/acpi

Signed-off-by: Alex Hung <alex.hung@canonical.com>
---
 src/Makefile.am            |   1 +
 src/acpi/dsddump/dsddump.c | 196 +++++++++++++++++++++++++++++++++++++
 2 files changed, 197 insertions(+)
 create mode 100644 src/acpi/dsddump/dsddump.c

Comments

Colin Ian King Jan. 11, 2019, 6:38 p.m. UTC | #1
On 09/01/2019 00:44, Alex Hung wrote:
> Buglink: https://bugs.launchpad.net/bugs/1433643
> 
> In addition to ACPI spec, this patch also refers to two documents:
> 
>  - Device Properties UUID For _DSD
>  - Hierarchical Data Extension UUID For _DSD
> 
> Both documents can be found @ http://uefi.org/acpi
> 
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am            |   1 +
>  src/acpi/dsddump/dsddump.c | 196 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 197 insertions(+)
>  create mode 100644 src/acpi/dsddump/dsddump.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 52c637dd..8339f306 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -80,6 +80,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/dmar/dmar.c 			\
>  	acpi/dppt/dppt.c 			\
>  	acpi/drtm/drtm.c 			\
> +	acpi/dsddump/dsddump.c			\
>  	acpi/ecdt/ecdt.c			\
>  	acpi/einj/einj.c			\
>  	acpi/erst/erst.c			\
> diff --git a/src/acpi/dsddump/dsddump.c b/src/acpi/dsddump/dsddump.c
> new file mode 100644
> index 00000000..611789b3
> --- /dev/null
> +++ b/src/acpi/dsddump/dsddump.c
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright (C) 2019 Canonical
> + *
> + * 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.
> + *
> + */
> +#include "fwts.h"
> +
> +#if defined(FWTS_HAS_ACPI)
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <inttypes.h>
> +#include "fwts_acpi_object_eval.h"
> +
> +#define DEVICE_PROPERITY_UUID "DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"
> +#define HIERARCHICAL_DATA_EXTENSION "DBB8E3E6-5886-4BA6-8795-1319F52A966B"
> +
> +/*
> + *  dsddump_init()
> + *	initialize ACPI
> + */
> +static int dsddump_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_init(fw) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot initialise ACPI.");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  dsddump_deinit
> + *	de-intialize ACPI
> + */
> +static int dsddump_deinit(fwts_framework *fw)
> +{
> +	return fwts_acpi_deinit(fw);
> +}
> +
> +static void print_package_element(fwts_framework *fw, ACPI_OBJECT *obj)
> +{
> +	char full_name[128];
> +	ACPI_STATUS status;
> +	ACPI_BUFFER buffer;
> +
> +	switch (obj->Type) {
> +		case ACPI_TYPE_INTEGER:
> +			fwts_log_info_verbatim(fw, "    Value: 0x%8.8" PRIx64, obj->Integer.Value);
> +			break;
> +		case ACPI_TYPE_STRING:
> +			fwts_log_info_verbatim(fw, "    Value: %s", obj->String.Pointer);
> +			break;
> +		case ACPI_TYPE_LOCAL_REFERENCE:
> +			status = AcpiGetName(obj->Reference.Handle, ACPI_FULL_PATHNAME, &buffer);
> +			if (ACPI_SUCCESS(status))
> +				fwts_log_info_verbatim(fw, "    Value: %s", full_name);
> +			break;
> +		default:
> +			fwts_log_info_verbatim(fw, "    Value: %s", "Unknown");
> +			break;
> +	}
> +}
> +
> +static void parse_device_properity(fwts_framework *fw, ACPI_OBJECT *pkg)
> +{
> +	uint32_t i;
> +
> +	fwts_log_info_verbatim(fw, "  Package:");
> +	for (i = 0; i < pkg->Package.Count; i++) {
> +		ACPI_OBJECT *element = &pkg->Package.Elements[i];
> +
> +		fwts_log_info_verbatim(fw, "    Key: %s", element->Package.Elements[0].String.Pointer);
> +
> +		if (element->Package.Elements[1].Type == ACPI_TYPE_PACKAGE) {
> +			ACPI_OBJECT *sub_pkg = &element->Package.Elements[1];
> +			uint32_t j;
> +
> +			for (j = 0; i < sub_pkg->Package.Count; j++)
> +				print_package_element(fw, &sub_pkg->Package.Elements[j]);
> +		} else
> +			print_package_element(fw, &element->Package.Elements[1]);
> +	}
> +}
> +
> +static void parse_hierarchical_data_ext(fwts_framework *fw, ACPI_OBJECT *pkg)
> +{
> +	uint32_t i;
> +
> +	fwts_log_info_verbatim(fw, "  Package:");
> +	for (i = 0; i < pkg->Package.Count; i++) {
> +		ACPI_OBJECT *element = &pkg->Package.Elements[i]> +		fwts_log_info_verbatim(fw, "    Key: %s",
element->Package.Elements[0].String.Pointer);
> +		print_package_element(fw, &element->Package.Elements[1]);
> +	}
> +}
> +
> +static void dsddump_package(
> +	fwts_framework *fw,
> +	ACPI_OBJECT *uuid,
> +	ACPI_OBJECT *pkg)
> +{
> +	char guid[37];
> +
> +	fwts_guid_buf_to_str(uuid->Buffer.Pointer, guid, sizeof(guid));
> +
> +	if (!strncmp(guid, DEVICE_PROPERITY_UUID, sizeof(guid))) {
> +		fwts_log_info_verbatim(fw, "  Device Properties UUID: %s", guid);
> +		parse_device_properity(fw, pkg);
> +	} else if (!strncmp(guid, HIERARCHICAL_DATA_EXTENSION, sizeof(guid))) {
> +		fwts_log_info_verbatim(fw, "  Hierarchical Data Extension UUID: %s", guid);
> +		parse_hierarchical_data_ext(fw, pkg);
> +	}
> +}
> +
> +static int dsddump_test1(fwts_framework *fw)
> +{
> +	const size_t name_len = 4;
> +	fwts_list_link	*item;
> +	fwts_list *objects;
> +	bool found = false;
> +
> +	if ((objects = fwts_acpi_object_get_names()) == NULL) {
> +		fwts_log_info(fw, "Cannot find any ACPI objects");
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_list_foreach(item, objects) {
> +		char *name = fwts_list_data(char *, item);
> +		const size_t len = strlen(name);
> +
> +		if (!strncmp("_DSD", name + len - name_len, name_len)) {
> +			ACPI_OBJECT_LIST arg_list;
> +			ACPI_OBJECT *obj;
> +			ACPI_BUFFER buf;
> +			uint32_t i;
> +			int ret;
> +
> +			arg_list.Count   = 0;
> +			arg_list.Pointer = NULL;
> +
> +			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 in the package to dump? */
> +			obj = buf.Pointer;
> +			if ((obj->Type == ACPI_TYPE_PACKAGE) &&
> +			    (obj->Package.Count) &&
> +			    (obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) &&
> +			    (obj->Package.Elements[0].Buffer.Pointer != NULL) &&
> +			    (obj->Package.Elements[0].Buffer.Length == 16)) {
> +
> +				fwts_log_info_verbatim(fw, "Name: %s", name);
> +				for (i = 0; i < obj->Package.Count; i += 2)
> +					dsddump_package(fw, &obj->Package.Elements[i], &obj->Package.Elements[i+1]);
> +
> +				fwts_log_nl(fw);
> +				found = true;
> +			}
> +			free(buf.Pointer);
> +		}
> +	}
> +
> +	if (!found)
> +		fwts_log_info_verbatim(fw, "No _DSD objects found.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test dsddump_tests[] = {
> +	{ dsddump_test1, "Dump ACPI _DSD (Device Specific Data)." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops dsddump_ops = {
> +	.description = "Dump ACPI _DSD (Device Specific Data).",
> +	.init        = dsddump_init,
> +	.deinit      = dsddump_deinit,
> +	.minor_tests = dsddump_tests
> +};
> +
> +FWTS_REGISTER("dsddump", &dsddump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS)
> +
> +#endif
> 


Looks OK to me. Do we have any _DSDD test data - can we add a regression
test for this too?

Thanks

Acked-by: Colin Ian King <colin.king@canonical.com>
Ivan Hu Jan. 14, 2019, 9:56 a.m. UTC | #2
On 1/9/19 8:44 AM, Alex Hung wrote:
> Buglink: https://bugs.launchpad.net/bugs/1433643
>
> In addition to ACPI spec, this patch also refers to two documents:
>
>  - Device Properties UUID For _DSD
>  - Hierarchical Data Extension UUID For _DSD
>
> Both documents can be found @ http://uefi.org/acpi
>
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am            |   1 +
>  src/acpi/dsddump/dsddump.c | 196 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 197 insertions(+)
>  create mode 100644 src/acpi/dsddump/dsddump.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 52c637dd..8339f306 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -80,6 +80,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/dmar/dmar.c 			\
>  	acpi/dppt/dppt.c 			\
>  	acpi/drtm/drtm.c 			\
> +	acpi/dsddump/dsddump.c			\
>  	acpi/ecdt/ecdt.c			\
>  	acpi/einj/einj.c			\
>  	acpi/erst/erst.c			\
> diff --git a/src/acpi/dsddump/dsddump.c b/src/acpi/dsddump/dsddump.c
> new file mode 100644
> index 00000000..611789b3
> --- /dev/null
> +++ b/src/acpi/dsddump/dsddump.c
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright (C) 2019 Canonical
> + *
> + * 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.
> + *
> + */
> +#include "fwts.h"
> +
> +#if defined(FWTS_HAS_ACPI)
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <inttypes.h>
> +#include "fwts_acpi_object_eval.h"
> +
> +#define DEVICE_PROPERITY_UUID "DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"
> +#define HIERARCHICAL_DATA_EXTENSION "DBB8E3E6-5886-4BA6-8795-1319F52A966B"
> +
> +/*
> + *  dsddump_init()
> + *	initialize ACPI
> + */
> +static int dsddump_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_init(fw) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot initialise ACPI.");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  dsddump_deinit
> + *	de-intialize ACPI
> + */
> +static int dsddump_deinit(fwts_framework *fw)
> +{
> +	return fwts_acpi_deinit(fw);
> +}
> +
> +static void print_package_element(fwts_framework *fw, ACPI_OBJECT *obj)
> +{
> +	char full_name[128];
> +	ACPI_STATUS status;
> +	ACPI_BUFFER buffer;
> +
> +	switch (obj->Type) {
> +		case ACPI_TYPE_INTEGER:
> +			fwts_log_info_verbatim(fw, "    Value: 0x%8.8" PRIx64, obj->Integer.Value);
> +			break;
> +		case ACPI_TYPE_STRING:
> +			fwts_log_info_verbatim(fw, "    Value: %s", obj->String.Pointer);
> +			break;
> +		case ACPI_TYPE_LOCAL_REFERENCE:
> +			status = AcpiGetName(obj->Reference.Handle, ACPI_FULL_PATHNAME, &buffer);
> +			if (ACPI_SUCCESS(status))
> +				fwts_log_info_verbatim(fw, "    Value: %s", full_name);
> +			break;
> +		default:
> +			fwts_log_info_verbatim(fw, "    Value: %s", "Unknown");
> +			break;
> +	}
> +}
> +
> +static void parse_device_properity(fwts_framework *fw, ACPI_OBJECT *pkg)
> +{
> +	uint32_t i;
> +
> +	fwts_log_info_verbatim(fw, "  Package:");
> +	for (i = 0; i < pkg->Package.Count; i++) {
> +		ACPI_OBJECT *element = &pkg->Package.Elements[i];
> +
> +		fwts_log_info_verbatim(fw, "    Key: %s", element->Package.Elements[0].String.Pointer);
> +
> +		if (element->Package.Elements[1].Type == ACPI_TYPE_PACKAGE) {
> +			ACPI_OBJECT *sub_pkg = &element->Package.Elements[1];
> +			uint32_t j;
> +
> +			for (j = 0; i < sub_pkg->Package.Count; j++)
> +				print_package_element(fw, &sub_pkg->Package.Elements[j]);
> +		} else
> +			print_package_element(fw, &element->Package.Elements[1]);
> +	}
> +}
> +
> +static void parse_hierarchical_data_ext(fwts_framework *fw, ACPI_OBJECT *pkg)
> +{
> +	uint32_t i;
> +
> +	fwts_log_info_verbatim(fw, "  Package:");
> +	for (i = 0; i < pkg->Package.Count; i++) {
> +		ACPI_OBJECT *element = &pkg->Package.Elements[i];
> +		fwts_log_info_verbatim(fw, "    Key: %s", element->Package.Elements[0].String.Pointer);
> +		print_package_element(fw, &element->Package.Elements[1]);
> +	}
> +}
> +
> +static void dsddump_package(
> +	fwts_framework *fw,
> +	ACPI_OBJECT *uuid,
> +	ACPI_OBJECT *pkg)
> +{
> +	char guid[37];
> +
> +	fwts_guid_buf_to_str(uuid->Buffer.Pointer, guid, sizeof(guid));
> +
> +	if (!strncmp(guid, DEVICE_PROPERITY_UUID, sizeof(guid))) {
> +		fwts_log_info_verbatim(fw, "  Device Properties UUID: %s", guid);
> +		parse_device_properity(fw, pkg);
> +	} else if (!strncmp(guid, HIERARCHICAL_DATA_EXTENSION, sizeof(guid))) {
> +		fwts_log_info_verbatim(fw, "  Hierarchical Data Extension UUID: %s", guid);
> +		parse_hierarchical_data_ext(fw, pkg);
> +	}
> +}
> +
> +static int dsddump_test1(fwts_framework *fw)
> +{
> +	const size_t name_len = 4;
> +	fwts_list_link	*item;
> +	fwts_list *objects;
> +	bool found = false;
> +
> +	if ((objects = fwts_acpi_object_get_names()) == NULL) {
> +		fwts_log_info(fw, "Cannot find any ACPI objects");
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_list_foreach(item, objects) {
> +		char *name = fwts_list_data(char *, item);
> +		const size_t len = strlen(name);
> +
> +		if (!strncmp("_DSD", name + len - name_len, name_len)) {
> +			ACPI_OBJECT_LIST arg_list;
> +			ACPI_OBJECT *obj;
> +			ACPI_BUFFER buf;
> +			uint32_t i;
> +			int ret;
> +
> +			arg_list.Count   = 0;
> +			arg_list.Pointer = NULL;
> +
> +			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 in the package to dump? */
> +			obj = buf.Pointer;
> +			if ((obj->Type == ACPI_TYPE_PACKAGE) &&
> +			    (obj->Package.Count) &&
> +			    (obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) &&
> +			    (obj->Package.Elements[0].Buffer.Pointer != NULL) &&
> +			    (obj->Package.Elements[0].Buffer.Length == 16)) {
> +
> +				fwts_log_info_verbatim(fw, "Name: %s", name);
> +				for (i = 0; i < obj->Package.Count; i += 2)
> +					dsddump_package(fw, &obj->Package.Elements[i], &obj->Package.Elements[i+1]);
> +
> +				fwts_log_nl(fw);
> +				found = true;
> +			}
> +			free(buf.Pointer);
> +		}
> +	}
> +
> +	if (!found)
> +		fwts_log_info_verbatim(fw, "No _DSD objects found.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test dsddump_tests[] = {
> +	{ dsddump_test1, "Dump ACPI _DSD (Device Specific Data)." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops dsddump_ops = {
> +	.description = "Dump ACPI _DSD (Device Specific Data).",
> +	.init        = dsddump_init,
> +	.deinit      = dsddump_deinit,
> +	.minor_tests = dsddump_tests
> +};
> +
> +FWTS_REGISTER("dsddump", &dsddump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS)
> +
> +#endif


Acked-by: Ivan Hu <ivan.hu@canonical.com>
diff mbox series

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 52c637dd..8339f306 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -80,6 +80,7 @@  fwts_SOURCES = main.c 				\
 	acpi/dmar/dmar.c 			\
 	acpi/dppt/dppt.c 			\
 	acpi/drtm/drtm.c 			\
+	acpi/dsddump/dsddump.c			\
 	acpi/ecdt/ecdt.c			\
 	acpi/einj/einj.c			\
 	acpi/erst/erst.c			\
diff --git a/src/acpi/dsddump/dsddump.c b/src/acpi/dsddump/dsddump.c
new file mode 100644
index 00000000..611789b3
--- /dev/null
+++ b/src/acpi/dsddump/dsddump.c
@@ -0,0 +1,196 @@ 
+/*
+ * Copyright (C) 2019 Canonical
+ *
+ * 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.
+ *
+ */
+#include "fwts.h"
+
+#if defined(FWTS_HAS_ACPI)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include "fwts_acpi_object_eval.h"
+
+#define DEVICE_PROPERITY_UUID "DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"
+#define HIERARCHICAL_DATA_EXTENSION "DBB8E3E6-5886-4BA6-8795-1319F52A966B"
+
+/*
+ *  dsddump_init()
+ *	initialize ACPI
+ */
+static int dsddump_init(fwts_framework *fw)
+{
+	if (fwts_acpi_init(fw) != FWTS_OK) {
+		fwts_log_error(fw, "Cannot initialise ACPI.");
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+/*
+ *  dsddump_deinit
+ *	de-intialize ACPI
+ */
+static int dsddump_deinit(fwts_framework *fw)
+{
+	return fwts_acpi_deinit(fw);
+}
+
+static void print_package_element(fwts_framework *fw, ACPI_OBJECT *obj)
+{
+	char full_name[128];
+	ACPI_STATUS status;
+	ACPI_BUFFER buffer;
+
+	switch (obj->Type) {
+		case ACPI_TYPE_INTEGER:
+			fwts_log_info_verbatim(fw, "    Value: 0x%8.8" PRIx64, obj->Integer.Value);
+			break;
+		case ACPI_TYPE_STRING:
+			fwts_log_info_verbatim(fw, "    Value: %s", obj->String.Pointer);
+			break;
+		case ACPI_TYPE_LOCAL_REFERENCE:
+			status = AcpiGetName(obj->Reference.Handle, ACPI_FULL_PATHNAME, &buffer);
+			if (ACPI_SUCCESS(status))
+				fwts_log_info_verbatim(fw, "    Value: %s", full_name);
+			break;
+		default:
+			fwts_log_info_verbatim(fw, "    Value: %s", "Unknown");
+			break;
+	}
+}
+
+static void parse_device_properity(fwts_framework *fw, ACPI_OBJECT *pkg)
+{
+	uint32_t i;
+
+	fwts_log_info_verbatim(fw, "  Package:");
+	for (i = 0; i < pkg->Package.Count; i++) {
+		ACPI_OBJECT *element = &pkg->Package.Elements[i];
+
+		fwts_log_info_verbatim(fw, "    Key: %s", element->Package.Elements[0].String.Pointer);
+
+		if (element->Package.Elements[1].Type == ACPI_TYPE_PACKAGE) {
+			ACPI_OBJECT *sub_pkg = &element->Package.Elements[1];
+			uint32_t j;
+
+			for (j = 0; i < sub_pkg->Package.Count; j++)
+				print_package_element(fw, &sub_pkg->Package.Elements[j]);
+		} else
+			print_package_element(fw, &element->Package.Elements[1]);
+	}
+}
+
+static void parse_hierarchical_data_ext(fwts_framework *fw, ACPI_OBJECT *pkg)
+{
+	uint32_t i;
+
+	fwts_log_info_verbatim(fw, "  Package:");
+	for (i = 0; i < pkg->Package.Count; i++) {
+		ACPI_OBJECT *element = &pkg->Package.Elements[i];
+		fwts_log_info_verbatim(fw, "    Key: %s", element->Package.Elements[0].String.Pointer);
+		print_package_element(fw, &element->Package.Elements[1]);
+	}
+}
+
+static void dsddump_package(
+	fwts_framework *fw,
+	ACPI_OBJECT *uuid,
+	ACPI_OBJECT *pkg)
+{
+	char guid[37];
+
+	fwts_guid_buf_to_str(uuid->Buffer.Pointer, guid, sizeof(guid));
+
+	if (!strncmp(guid, DEVICE_PROPERITY_UUID, sizeof(guid))) {
+		fwts_log_info_verbatim(fw, "  Device Properties UUID: %s", guid);
+		parse_device_properity(fw, pkg);
+	} else if (!strncmp(guid, HIERARCHICAL_DATA_EXTENSION, sizeof(guid))) {
+		fwts_log_info_verbatim(fw, "  Hierarchical Data Extension UUID: %s", guid);
+		parse_hierarchical_data_ext(fw, pkg);
+	}
+}
+
+static int dsddump_test1(fwts_framework *fw)
+{
+	const size_t name_len = 4;
+	fwts_list_link	*item;
+	fwts_list *objects;
+	bool found = false;
+
+	if ((objects = fwts_acpi_object_get_names()) == NULL) {
+		fwts_log_info(fw, "Cannot find any ACPI objects");
+		return FWTS_ERROR;
+	}
+
+	fwts_list_foreach(item, objects) {
+		char *name = fwts_list_data(char *, item);
+		const size_t len = strlen(name);
+
+		if (!strncmp("_DSD", name + len - name_len, name_len)) {
+			ACPI_OBJECT_LIST arg_list;
+			ACPI_OBJECT *obj;
+			ACPI_BUFFER buf;
+			uint32_t i;
+			int ret;
+
+			arg_list.Count   = 0;
+			arg_list.Pointer = NULL;
+
+			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 in the package to dump? */
+			obj = buf.Pointer;
+			if ((obj->Type == ACPI_TYPE_PACKAGE) &&
+			    (obj->Package.Count) &&
+			    (obj->Package.Elements[0].Type == ACPI_TYPE_BUFFER) &&
+			    (obj->Package.Elements[0].Buffer.Pointer != NULL) &&
+			    (obj->Package.Elements[0].Buffer.Length == 16)) {
+
+				fwts_log_info_verbatim(fw, "Name: %s", name);
+				for (i = 0; i < obj->Package.Count; i += 2)
+					dsddump_package(fw, &obj->Package.Elements[i], &obj->Package.Elements[i+1]);
+
+				fwts_log_nl(fw);
+				found = true;
+			}
+			free(buf.Pointer);
+		}
+	}
+
+	if (!found)
+		fwts_log_info_verbatim(fw, "No _DSD objects found.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test dsddump_tests[] = {
+	{ dsddump_test1, "Dump ACPI _DSD (Device Specific Data)." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops dsddump_ops = {
+	.description = "Dump ACPI _DSD (Device Specific Data).",
+	.init        = dsddump_init,
+	.deinit      = dsddump_deinit,
+	.minor_tests = dsddump_tests
+};
+
+FWTS_REGISTER("dsddump", &dsddump_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS)
+
+#endif