All of lore.kernel.org
 help / color / mirror / Atom feed
From: Simon Glass <sjg@chromium.org>
To: u-boot@lists.denx.de
Subject: [PATCH v3 19/35] acpi: Support writing Device Properties objects via _DSD
Date: Sat, 13 Jun 2020 20:55:07 -0600	[thread overview]
Message-ID: <20200614025523.40183-9-sjg@chromium.org> (raw)
In-Reply-To: <20200614025523.40183-1-sjg@chromium.org>

More complex device properties can be provided to drivers via a
device-specific data (_DSD) object.

To create this we need to build it up in a separate data structure and
then generate the ACPI code, due to its recursive nature.

Add an implementation of this.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com>
---

Changes in v3:
- Allow the name parameter to be NULL
- Add error checking to acpi_dp_add_integer_array()
- Fix 'acpi_device.v' typo
- Drop unused ACPI_CPU_STRING

 include/acpi/acpi_dp.h | 216 ++++++++++++++++++++++
 include/acpi/acpigen.h |   1 +
 lib/acpi/Makefile      |   1 +
 lib/acpi/acpi_dp.c     | 323 ++++++++++++++++++++++++++++++++
 test/dm/Makefile       |   1 +
 test/dm/acpi_dp.c      | 405 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 947 insertions(+)
 create mode 100644 include/acpi/acpi_dp.h
 create mode 100644 lib/acpi/acpi_dp.c
 create mode 100644 test/dm/acpi_dp.c

diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h
new file mode 100644
index 0000000000..3fd048e111
--- /dev/null
+++ b/include/acpi/acpi_dp.h
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Device properties, a temporary data structure for adding to ACPI code
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file acpi_device.h
+ */
+
+#ifndef __ACPI_DP_H
+#define __ACPI_DP_H
+
+struct acpi_ctx;
+
+/*
+ * Writing Device Properties objects via _DSD
+ *
+ * This is described in ACPI 6.3 section 6.2.5
+ *
+ * This provides a structure to handle nested device-specific data which ends
+ * up in a _DSD table.
+ *
+ * https://www.kernel.org/doc/html/latest/firmware-guide/acpi/DSD-properties-rules.html
+ * https://uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
+ * https://uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf
+ *
+ * The Device Property Hierarchy can be multiple levels deep with multiple
+ * children possible in each level.  In order to support this flexibility
+ * the device property hierarchy must be built up before being written out.
+ *
+ * For example:
+ *
+ * // Child table with string and integer
+ * struct acpi_dp *child = acpi_dp_new_table("CHLD");
+ * acpi_dp_add_string(child, "childstring", "CHILD");
+ * acpi_dp_add_integer(child, "childint", 100);
+ *
+ * // _DSD table with integer and gpio and child pointer
+ * struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
+ * acpi_dp_add_integer(dsd, "number1", 1);
+ * acpi_dp_add_gpio(dsd, "gpio", "\_SB.PCI0.GPIO", 0, 0, 1);
+ * acpi_dp_add_child(dsd, "child", child);
+ *
+ * // Write entries into SSDT and clean up resources
+ * acpi_dp_write(dsd);
+ *
+ * Name(_DSD, Package() {
+ *   ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
+ *   Package() {
+ *     Package() { "gpio", Package() { \_SB.PCI0.GPIO, 0, 0, 0 } }
+ *     Package() { "number1", 1 }
+ *   }
+ *   ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b")
+ *   Package() {
+ *     Package() { "child", CHLD }
+ *   }
+ * }
+ * Name(CHLD, Package() {
+ *   ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
+ *   Package() {
+ *     Package() { "childstring", "CHILD" }
+ *     Package() { "childint", 100 }
+ *   }
+ * }
+ */
+
+#define ACPI_DP_UUID		"daffd814-6eba-4d8c-8a91-bc9bbf4aa301"
+#define ACPI_DP_CHILD_UUID	"dbb8e3e6-5886-4ba6-8795-1319f52a966b"
+
+/**
+ * enum acpi_dp_type - types of device property objects
+ *
+ * These refer to the types defined by struct acpi_dp below
+ *
+ * @ACPI_DP_TYPE_UNKNOWN: Unknown / do not use
+ * @ACPI_DP_TYPE_INTEGER: Integer value (u64) in @integer
+ * @ACPI_DP_TYPE_STRING: String value in @string
+ * @ACPI_DP_TYPE_REFERENCE: Reference to another object, with value in @string
+ * @ACPI_DP_TYPE_TABLE: Type for a top-level table which may have children
+ * @ACPI_DP_TYPE_ARRAY: Array of items with first item in @array and following
+ *	items linked from that item's @next
+ * @ACPI_DP_TYPE_CHILD: Child object, with siblings in that child's @next
+ */
+enum acpi_dp_type {
+	ACPI_DP_TYPE_UNKNOWN,
+	ACPI_DP_TYPE_INTEGER,
+	ACPI_DP_TYPE_STRING,
+	ACPI_DP_TYPE_REFERENCE,
+	ACPI_DP_TYPE_TABLE,
+	ACPI_DP_TYPE_ARRAY,
+	ACPI_DP_TYPE_CHILD,
+};
+
+/**
+ * struct acpi_dp - ACPI device properties
+ *
+ * @type: Table type
+ * @name: Name of object, typically _DSD but could be CHLD for a child object.
+ *	This can be NULL if there is no name
+ * @next: Next object in list (next array element or next sibling)
+ * @child: Pointer to first child, if @type == ACPI_DP_TYPE_CHILD, else NULL
+ * @array: First array element, if @type == ACPI_DP_TYPE_ARRAY, else NULL
+ * @integer: Integer value of the property, if @type == ACPI_DP_TYPE_INTEGER
+ * @string: String value of the property, if @type == ACPI_DP_TYPE_STRING;
+ *	child name if @type == ACPI_DP_TYPE_CHILD;
+ *	reference name if @type == ACPI_DP_TYPE_REFERENCE;
+ */
+struct acpi_dp {
+	enum acpi_dp_type type;
+	const char *name;
+	struct acpi_dp *next;
+	union {
+		struct acpi_dp *child;
+		struct acpi_dp *array;
+	};
+	union {
+		u64 integer;
+		const char *string;
+	};
+};
+
+/**
+ * acpi_dp_new_table() - Start a new Device Property table
+ *
+ * @ref: ACPI reference (e.g. "_DSD")
+ * @return pointer to table, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_new_table(const char *ref);
+
+/**
+ * acpi_dp_add_integer() - Add integer Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @value: Integer value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
+				    u64 value);
+
+/**
+ * acpi_dp_add_string() - Add string Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @string: String value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
+				   const char *string);
+
+/**
+ * acpi_dp_add_reference() - Add reference Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @reference: Reference value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
+				      const char *reference);
+
+/**
+ * acpi_dp_add_array() - Add array Device Property
+ *
+ * A new node is added to the end of the property list of @dp, with the array
+ * attached to that.
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array);
+
+/**
+ * acpi_dp_add_integer_array() - Add an array of integers
+ *
+ * A new node is added to the end of the property list of @dp, with the array
+ * attached to that. Each element of the array becomes a new node.
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @return pointer to new array node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
+					  u64 *array, int len);
+
+/**
+ * acpi_dp_add_child() - Add a child table of Device Properties
+ *
+ * A new node is added as a child of @dp
+ *
+ * @dp: Table to add this child to
+ * @name: Name of child, or NULL for none
+ * @child: Child node to add
+ * @return pointer to new array node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
+				  struct acpi_dp *child);
+
+/**
+ * acpi_dp_write() - Write Device Property hierarchy and clean up resources
+ *
+ * This writes the table using acpigen and then frees it
+ *
+ * @table: Table to write
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table);
+
+#endif
diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h
index f45a19714b..40cd72504a 100644
--- a/include/acpi/acpigen.h
+++ b/include/acpi/acpigen.h
@@ -31,6 +31,7 @@ enum {
 	PACKAGE_OP		= 0x12,
 	DUAL_NAME_PREFIX	= 0x2e,
 	MULTI_NAME_PREFIX	= 0x2f,
+	ROOT_PREFIX		= 0x5c,
 };
 
 /**
diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile
index 85a1f774ad..5c2f793701 100644
--- a/lib/acpi/Makefile
+++ b/lib/acpi/Makefile
@@ -3,4 +3,5 @@
 
 obj-y += acpigen.o
 obj-y += acpi_device.o
+obj-y += acpi_dp.o
 obj-y += acpi_table.o
diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c
new file mode 100644
index 0000000000..ebbc5d5538
--- /dev/null
+++ b/lib/acpi/acpi_dp.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of tables for particular device types
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file acpi_device.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <dm/acpi.h>
+
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+				const struct acpi_dp *array);
+
+static void acpi_dp_write_value(struct acpi_ctx *ctx,
+				const struct acpi_dp *prop)
+{
+	switch (prop->type) {
+	case ACPI_DP_TYPE_INTEGER:
+		acpigen_write_integer(ctx, prop->integer);
+		break;
+	case ACPI_DP_TYPE_STRING:
+	case ACPI_DP_TYPE_CHILD:
+		acpigen_write_string(ctx, prop->string);
+		break;
+	case ACPI_DP_TYPE_REFERENCE:
+		acpigen_emit_namestring(ctx, prop->string);
+		break;
+	case ACPI_DP_TYPE_ARRAY:
+		acpi_dp_write_array(ctx, prop->array);
+		break;
+	default:
+		break;
+	}
+}
+
+/* Package (2) { "prop->name", VALUE } */
+static void acpi_dp_write_property(struct acpi_ctx *ctx,
+				   const struct acpi_dp *prop)
+{
+	acpigen_write_package(ctx, 2);
+	acpigen_write_string(ctx, prop->name);
+	acpi_dp_write_value(ctx, prop);
+	acpigen_pop_len(ctx);
+}
+
+/* Write array of Device Properties */
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+				const struct acpi_dp *array)
+{
+	const struct acpi_dp *dp;
+	char *pkg_count;
+
+	/* Package element count determined as it is populated */
+	pkg_count = acpigen_write_package(ctx, 0);
+
+	/*
+	 * Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array.
+	 * DP_TYPE_TABLE does not have a value to be written. Thus, start
+	 * the loop from next type in the array.
+	 */
+	for (dp = array->next; dp; dp = dp->next) {
+		acpi_dp_write_value(ctx, dp);
+		(*pkg_count)++;
+	}
+
+	acpigen_pop_len(ctx);
+}
+
+static void acpi_dp_free(struct acpi_dp *dp)
+{
+	assert(dp);
+	while (dp) {
+		struct acpi_dp *p = dp->next;
+
+		switch (dp->type) {
+		case ACPI_DP_TYPE_CHILD:
+			acpi_dp_free(dp->child);
+			break;
+		case ACPI_DP_TYPE_ARRAY:
+			acpi_dp_free(dp->array);
+			break;
+		default:
+			break;
+		}
+
+		free(dp);
+		dp = p;
+	}
+}
+
+int acpi_dp_write_(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+	struct acpi_dp *dp, *prop;
+	char *dp_count, *prop_count = NULL;
+	int child_count = 0;
+	int ret;
+
+	assert(table);
+	if (table->type != ACPI_DP_TYPE_TABLE)
+		return 0;
+
+	/* Name (name) */
+	acpigen_write_name(ctx, table->name);
+
+	/* Device Property list starts with the next entry */
+	prop = table->next;
+
+	/* Package (DP), default to assuming no properties or children */
+	dp_count = acpigen_write_package(ctx, 0);
+
+	/* Print base properties */
+	for (dp = prop; dp; dp = dp->next) {
+		if (dp->type == ACPI_DP_TYPE_CHILD) {
+			child_count++;
+		} else {
+			/*
+			 * The UUID and package is only added when
+			 * we come across the first property.  This
+			 * is to avoid creating a zero-length package
+			 * in situations where there are only children.
+			 */
+			if (!prop_count) {
+				*dp_count += 2;
+				/* ToUUID (ACPI_DP_UUID) */
+				ret = acpigen_write_uuid(ctx, ACPI_DP_UUID);
+				if (ret)
+					return log_msg_ret("touuid", ret);
+				/*
+				 * Package (PROP), element count determined as
+				 * it is populated
+				 */
+				prop_count = acpigen_write_package(ctx, 0);
+			}
+			(*prop_count)++;
+			acpi_dp_write_property(ctx, dp);
+		}
+	}
+
+	if (prop_count) {
+		/* Package (PROP) length, if a package was written */
+		acpigen_pop_len(ctx);
+	}
+
+	if (child_count) {
+		/* Update DP package count to 2 or 4 */
+		*dp_count += 2;
+		/* ToUUID (ACPI_DP_CHILD_UUID) */
+		ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID);
+		if (ret)
+			return log_msg_ret("child uuid", ret);
+
+		/* Print child pointer properties */
+		acpigen_write_package(ctx, child_count);
+
+		for (dp = prop; dp; dp = dp->next)
+			if (dp->type == ACPI_DP_TYPE_CHILD)
+				acpi_dp_write_property(ctx, dp);
+		/* Package (CHILD) length */
+		acpigen_pop_len(ctx);
+	}
+
+	/* Package (DP) length */
+	acpigen_pop_len(ctx);
+
+	/* Recursively parse children into separate tables */
+	for (dp = prop; dp; dp = dp->next) {
+		if (dp->type == ACPI_DP_TYPE_CHILD) {
+			ret = acpi_dp_write_(ctx, dp->child);
+			if (ret)
+				return log_msg_ret("dp child", ret);
+		}
+	}
+
+	return 0;
+}
+
+int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+	int ret;
+
+	ret = acpi_dp_write_(ctx, table);
+
+	/* Clean up */
+	acpi_dp_free(table);
+
+	if (ret)
+		return log_msg_ret("write", ret);
+
+	return 0;
+}
+
+static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type,
+				   const char *name)
+{
+	struct acpi_dp *new;
+
+	new = malloc(sizeof(struct acpi_dp));
+	if (!new)
+		return NULL;
+
+	memset(new, '\0', sizeof(*new));
+	new->type = type;
+	new->name = name;
+
+	if (dp) {
+		/* Add to end of property list */
+		while (dp->next)
+			dp = dp->next;
+		dp->next = new;
+	}
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_new_table(const char *name)
+{
+	return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name);
+}
+
+struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
+				    u64 value)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name);
+
+	if (new)
+		new->integer = value;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
+				   const char *string)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name);
+	if (new)
+		new->string = string;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
+				      const char *reference)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name);
+	if (new)
+		new->string = reference;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
+				  struct acpi_dp *child)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	if (child->type != ACPI_DP_TYPE_TABLE)
+		return NULL;
+
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name);
+	if (new) {
+		new->child = child;
+		new->string = child->name;
+	}
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	assert(array);
+	if (array->type != ACPI_DP_TYPE_TABLE)
+		return NULL;
+
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name);
+	if (new)
+		new->array = array;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
+					  u64 *array, int len)
+{
+	struct acpi_dp *dp_array;
+	int i;
+
+	assert(dp);
+	if (len <= 0)
+		return NULL;
+
+	dp_array = acpi_dp_new_table(name);
+	if (!dp_array)
+		return NULL;
+
+	for (i = 0; i < len; i++)
+		if (!acpi_dp_add_integer(dp_array, NULL, array[i]))
+			break;
+
+	if (!acpi_dp_add_array(dp, dp_array))
+		return NULL;
+
+	return dp_array;
+}
diff --git a/test/dm/Makefile b/test/dm/Makefile
index e3e0cccf01..eb62faa0e3 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_UT_DM) += core.o
 ifneq ($(CONFIG_SANDBOX),)
 obj-$(CONFIG_ACPIGEN) += acpi.o
 obj-$(CONFIG_ACPIGEN) += acpigen.o
+obj-$(CONFIG_ACPIGEN) += acpi_dp.o
 obj-$(CONFIG_SOUND) += audio.o
 obj-$(CONFIG_BLK) += blk.o
 obj-$(CONFIG_BOARD) += board.o
diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c
new file mode 100644
index 0000000000..c11394786f
--- /dev/null
+++ b/test/dm/acpi_dp.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI code generation via a device-property table
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+#define TEST_INT8	0x7d
+#define TEST_INT16	0x2345
+#define TEST_INT32	0x12345678
+#define TEST_INT64	0x4567890123456
+#define TEST_STR	"testing acpi strings"
+#define TEST_REF	"\\SB.I2C0.TPM2"
+#define EXPECT_REF	"SB__I2C0TPM2"
+
+static int alloc_context(struct acpi_ctx **ctxp)
+{
+	struct acpi_ctx *ctx;
+
+	*ctxp = NULL;
+	ctx = malloc(sizeof(*ctx));
+	if (!ctx)
+		return -ENOMEM;
+	memset(ctx, '\0', sizeof(*ctx));
+	ctx->current = malloc(500);
+	if (!ctx->current)
+		return -ENOMEM;
+	*ctxp = ctx;
+
+	return 0;
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+	free(*ctxp);
+	*ctxp = NULL;
+}
+
+/**
+ * get_length() - decode a three-byte length field
+ *
+ * @ptr: Length encoded as per ACPI
+ * @return decoded length, or -EINVAL on error
+ */
+static int get_length(u8 *ptr)
+{
+	if (!(*ptr & 0x80))
+		return -EINVAL;
+
+	return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12;
+}
+
+/* Test emitting an empty table */
+static int dm_test_acpi_dp_new_table(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(10, acpigen_get_current(ctx) - ptr);
+	ut_asserteq(NAME_OP, *(u8 *)ptr);
+	ut_asserteq_strn("FRED", (char *)ptr + 1);
+	ut_asserteq(PACKAGE_OP, ptr[5]);
+	ut_asserteq(4, get_length(ptr + 6));
+	ut_asserteq(0, ptr[9]);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_new_table, 0);
+
+/* Test emitting an integer */
+static int dm_test_acpi_dp_int(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	char uuid[UUID_STR_LEN + 1];
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(54, acpigen_get_current(ctx) - ptr);
+	ut_asserteq(NAME_OP, *(u8 *)ptr);
+	ut_asserteq_strn("FRED", (char *)ptr + 1);
+	ut_asserteq(PACKAGE_OP, ptr[5]);
+	ut_asserteq(48, get_length(ptr + 6));
+	ut_asserteq(2, ptr[9]);
+
+	/* UUID */
+	ut_asserteq(BUFFER_OP, ptr[10]);
+	ut_asserteq(22, get_length(ptr + 11));
+	ut_asserteq(WORD_PREFIX, ptr[14]);
+	ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+	uuid_bin_to_str(ptr + 17, uuid, 1);
+	ut_asserteq_str(ACPI_DP_UUID, uuid);
+
+	/* Container package */
+	ut_asserteq(PACKAGE_OP, ptr[33]);
+	ut_asserteq(20, get_length(ptr + 34));
+	ut_asserteq(1, ptr[37]);
+
+	/* Package with name and (integer) value */
+	ut_asserteq(PACKAGE_OP, ptr[38]);
+	ut_asserteq(15, get_length(ptr + 39));
+	ut_asserteq(2, ptr[42]);
+	ut_asserteq(STRING_PREFIX, ptr[43]);
+	ut_asserteq_str("MARY", (char *)ptr + 44);
+
+	ut_asserteq(DWORD_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int, 0);
+
+/* Test emitting a 64-bit integer */
+static int dm_test_acpi_dp_int64(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(58, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(QWORD_PREFIX, ptr[49]);
+	ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int64, 0);
+
+/* Test emitting a 16-bit integer */
+static int dm_test_acpi_dp_int16(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(52, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(WORD_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int16, 0);
+
+/* Test emitting a 8-bit integer */
+static int dm_test_acpi_dp_int8(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(51, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(BYTE_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT8, ptr[50]);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int8, 0);
+
+/* Test emitting multiple values */
+static int dm_test_acpi_dp_multiple(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16));
+	ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR));
+	ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(110, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(WORD_PREFIX, ptr[0x32]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33)));
+	ut_asserteq(STRING_PREFIX, ptr[0x3f]);
+	ut_asserteq_str(TEST_STR, (char *)ptr + 0x40);
+	ut_asserteq(ROOT_PREFIX, ptr[0x5f]);
+	ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]);
+	ut_asserteq(3, ptr[0x61]);
+	ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_multiple, 0);
+
+/* Test emitting an array */
+static int dm_test_acpi_dp_array(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u64 speed[4];
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	speed[0] = TEST_INT8;
+	speed[1] = TEST_INT16;
+	speed[2] = TEST_INT32;
+	speed[3] = TEST_INT64;
+	ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed,
+						   ARRAY_SIZE(speed)));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(75, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(BYTE_PREFIX, ptr[0x38]);
+	ut_asserteq(TEST_INT8, ptr[0x39]);
+
+	ut_asserteq(WORD_PREFIX, ptr[0x3a]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b)));
+
+	ut_asserteq(DWORD_PREFIX, ptr[0x3d]);
+	ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e)));
+
+	ut_asserteq(QWORD_PREFIX, ptr[0x42]);
+	ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_array, 0);
+
+/* Test emitting a child */
+static int dm_test_acpi_dp_child(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp, *child1, *child2;
+	char uuid[UUID_STR_LEN + 1];
+	u8 *ptr, *pptr;
+	int i;
+
+	ut_assertok(alloc_context(&ctx));
+
+	child1 = acpi_dp_new_table("child");
+	ut_assertnonnull(child1);
+	ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16));
+
+	child2 = acpi_dp_new_table("child");
+	ut_assertnonnull(child2);
+	ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1));
+	ut_assertnonnull(acpi_dp_add_child(dp, "john", child2));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(178, acpigen_get_current(ctx) - ptr);
+
+	/* UUID for child extension using Hierarchical Data Extension UUID */
+	ut_asserteq(BUFFER_OP, ptr[10]);
+	ut_asserteq(22, get_length(ptr + 11));
+	ut_asserteq(WORD_PREFIX, ptr[14]);
+	ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+	uuid_bin_to_str(ptr + 17, uuid, 1);
+	ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid);
+
+	/* Package with two children */
+	ut_asserteq(PACKAGE_OP, ptr[0x21]);
+	ut_asserteq(0x28, get_length(ptr + 0x22));
+	ut_asserteq(2, ptr[0x25]);
+
+	/* First we expect the two children as string/value */
+	pptr = ptr + 0x26;
+	for (i = 0; i < 2; i++) {
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(0x11, get_length(pptr + 1));
+		ut_asserteq(2, pptr[4]);
+		ut_asserteq(STRING_PREFIX, pptr[5]);
+		ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6);
+		ut_asserteq(STRING_PREFIX, pptr[11]);
+		ut_asserteq_str("child", (char *)pptr + 12);
+		pptr += 0x12;
+	}
+
+	/* Write the two children */
+	ut_asserteq(0x4a, pptr - ptr);
+	for (i = 0; i < 2; i++) {
+		const char *prop = i ? "age" : "height";
+		const int datalen = i ? 1 : 2;
+		int len = strlen(prop) + 1;
+
+		ut_asserteq(NAME_OP, pptr[0]);
+		ut_asserteq_strn("chil", (char *)pptr + 1);
+		ut_asserteq(PACKAGE_OP, pptr[5]);
+		ut_asserteq(0x27 + len + datalen, get_length(pptr + 6));
+		ut_asserteq(2, pptr[9]);
+
+		/* UUID */
+		ut_asserteq(BUFFER_OP, pptr[10]);
+		ut_asserteq(22, get_length(pptr + 11));
+		ut_asserteq(WORD_PREFIX, pptr[14]);
+		ut_asserteq(16, get_unaligned((u16 *)(pptr + 15)));
+		uuid_bin_to_str(pptr + 17, uuid, 1);
+		ut_asserteq_str(ACPI_DP_UUID, uuid);
+		pptr += 33;
+
+		/* Containing package */
+		ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr);
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(0xb + len + datalen, get_length(pptr + 1));
+		ut_asserteq(1, pptr[4]);
+
+		/* Package containing the property-name string and the value */
+		pptr += 5;
+		ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr);
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(6 + len + datalen, get_length(pptr + 1));
+		ut_asserteq(2, pptr[4]);
+
+		ut_asserteq(STRING_PREFIX, pptr[5]);
+		ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6);
+		pptr += 6 + len;
+		if (i) {
+			ut_asserteq(BYTE_PREFIX, pptr[0]);
+			ut_asserteq(TEST_INT8, pptr[1]);
+		} else {
+			ut_asserteq(WORD_PREFIX, pptr[0]);
+			ut_asserteq(TEST_INT16,
+				    get_unaligned((u16 *)(pptr + 1)));
+		}
+		pptr += 1 + datalen;
+	}
+	ut_asserteq(178, pptr - ptr);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_child, 0);
-- 
2.27.0.290.gba653c62da-goog

  parent reply	other threads:[~2020-06-14  2:55 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-14  2:54 [PATCH v3 00/35] dm: Add programmatic generation of ACPI tables (part B) Simon Glass
2020-06-14  2:54 ` [PATCH v3 01/35] dm: core: Add an ACPI name for the root node Simon Glass
2020-06-14  2:54 ` [PATCH v3 02/35] acpi: Add a function to get a device path and scope Simon Glass
2020-06-14  2:54 ` [PATCH v3 03/35] acpi: Add a way to check device status Simon Glass
2020-06-28  9:13   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 04/35] irq: Add a method to convert an interrupt to ACPI Simon Glass
2020-06-28  9:13   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 05/35] acpi: Support generation of ACPI code Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-28  9:13   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 06/35] acpi: Support generation of interrupt descriptor Simon Glass
2020-06-28  9:55   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 07/35] gpio: Add a method to convert a GPIO to ACPI Simon Glass
2020-06-28  9:55   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 08/35] acpi: Support string output Simon Glass
2020-06-28  9:55   ` Bin Meng
2020-07-07 19:12     ` Simon Glass
2020-06-14  2:54 ` [PATCH v3 09/35] acpi: Support generation of GPIO descriptor Simon Glass
2020-06-28  9:55   ` Bin Meng
2020-07-07 19:12     ` Simon Glass
2020-06-14  2:54 ` [PATCH v3 10/35] acpi: Support generation of a GPIO/irq for a device Simon Glass
2020-06-28  9:55   ` Bin Meng
2020-06-14  2:54 ` [PATCH v3 11/35] acpi: Support generation of I2C descriptor Simon Glass
2020-06-29  1:27   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 12/35] acpi: Support generation of SPI descriptor Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  1:27   ` Bin Meng
2020-07-07 19:12     ` Simon Glass
2020-06-14  2:55 ` [PATCH v3 13/35] acpigen: Support writing a length Simon Glass
2020-06-29  1:27   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 14/35] acpigen: Support writing a package Simon Glass
2020-06-29  1:27   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 15/35] acpi: Support writing an integer Simon Glass
2020-06-29  1:27   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 16/35] acpi: Support writing a string Simon Glass
2020-06-29  2:07   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 17/35] acpi: Support writing a name Simon Glass
2020-06-29  2:07   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 18/35] acpi: Support writing a UUID Simon Glass
2020-06-29  2:07   ` Bin Meng
2020-06-14  2:55 ` Simon Glass [this message]
2020-06-29  2:07   ` [PATCH v3 19/35] acpi: Support writing Device Properties objects via _DSD Bin Meng
2020-06-14  2:55 ` [PATCH v3 20/35] acpi: Support writing a GPIO Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  2:07   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 21/35] acpi: Support copying properties from device tree to ACPI Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  3:00   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 22/35] acpi: Add support for various misc ACPI opcodes Simon Glass
2020-06-29  3:00   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 23/35] acpi: Add support for writing a Power Resource Simon Glass
2020-06-29  3:00   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 24/35] acpi: Add support for writing a GPIO power sequence Simon Glass
2020-06-29  3:00   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 25/35] acpi: Add support for a generic " Simon Glass
2020-06-14  2:55 ` [PATCH v3 26/35] acpi: Add support for SSDT generation Simon Glass
2020-06-29  3:36   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 27/35] x86: acpi: Move MADT down a bit Simon Glass
2020-06-29  3:36   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 28/35] acpi: Record the items added to SSDT Simon Glass
2020-06-29  3:36   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 29/35] acpi: Support ordering SSDT data by device Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  3:36   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 30/35] x86: Allow devices to write an SSDT Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  3:36   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 31/35] acpi: Add support for DSDT generation Simon Glass
2020-06-29  5:39   ` Bin Meng
2020-07-07 19:12     ` Simon Glass
2020-06-14  2:55 ` [PATCH v3 32/35] x86: Allow devices to write to DSDT Simon Glass
2020-06-16 11:31   ` Wolfgang Wallner
2020-06-29  5:39   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 33/35] pci: Avoid a crash in device_is_on_pci_bus() Simon Glass
2020-06-29  5:39   ` Bin Meng
2020-06-14  2:55 ` [PATCH v3 34/35] dm: acpi: Enhance acpi_get_name() Simon Glass
2020-06-29  5:39   ` Bin Meng
2020-07-07 19:12     ` Simon Glass
2020-06-14  2:55 ` [PATCH v3 35/35] acpi: Add an acpi command to list/dump generated ACPI items Simon Glass
2020-06-29  5:39   ` Bin Meng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200614025523.40183-9-sjg@chromium.org \
    --to=sjg@chromium.org \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.