
Hi Simon,
On Sun, Jun 14, 2020 at 10:55 AM Simon Glass sjg@chromium.org wrote:
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.
- 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
nits: /* */
- 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
nits: /* */
- 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
nits: /* */
- 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
new child node
- */
+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)
This does not look like a public API but an internal helper. Can we rename this to acpi_dp_write_internal or acpi_dp_write_helper, and make it static?
+{
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);
nits: a random magic number for the context size?
if (!ctx->current)
return -ENOMEM;
*ctxp = ctx;
return 0;
+}
+static void free_context(struct acpi_ctx **ctxp) +{
free(*ctxp);
*ctxp = NULL;
+}
Can we make the above 2 routines a public API for test codes only?
+/**
- 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;
+}
This same function has repeated in various files. This merites a new API in the ACPI core.
+/* 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);
Regards, Bin