summaryrefslogtreecommitdiff
path: root/test/dm
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2020-07-07 13:11:56 -0600
committerBin Meng <bmeng.cn@gmail.com>2020-07-17 14:32:24 +0800
commit0e5a0a00d6e44dc0c7e1466ceb3e452b43ceeb1b (patch)
treedf19c9ee9a3d00dc9acb646541dcd7ad7d250287 /test/dm
parent29df845204e6b67583491b3c9883432c3a74d923 (diff)
acpi: Support writing Device Properties objects via _DSD
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> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'test/dm')
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/acpi.h29
-rw-r--r--test/dm/acpi_dp.c385
-rw-r--r--test/dm/acpigen.c39
4 files changed, 432 insertions, 22 deletions
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 5641070ea1..b03c96da06 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.h b/test/dm/acpi.h
new file mode 100644
index 0000000000..885dff85d3
--- /dev/null
+++ b/test/dm/acpi.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common functions for ACPI tests
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __TEST_DM_ACPI_H
+#define __TEST_DM_ACPI_H
+
+/**
+ * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size
+ *
+ * @ctxp: Returns allocated context
+ * @size: Size to allocate in bytes
+ * @return 0 if OK, -ENOMEM if out of memory
+ */
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size);
+
+/**
+ * acpi_test_get_length() - decode a three-byte length field
+ *
+ * @ptr: Length encoded as per ACPI
+ * @return decoded length, or -EINVAL on error
+ */
+int acpi_test_get_length(u8 *ptr);
+
+#endif /*__TEST_DM_ACPI_H */
diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c
new file mode 100644
index 0000000000..28696aaff6
--- /dev/null
+++ b/test/dm/acpi_dp.c
@@ -0,0 +1,385 @@
+// 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>
+#include "acpi.h"
+
+/* Maximum size of the ACPI context needed for most tests */
+#define ACPI_CONTEXT_SIZE 500
+
+#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)
+{
+ return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+
+ return 0;
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+ free(*ctxp);
+ *ctxp = NULL;
+}
+
+/* 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, acpi_test_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, acpi_test_get_length(ptr + 6));
+ ut_asserteq(2, ptr[9]);
+
+ /* UUID */
+ ut_asserteq(BUFFER_OP, ptr[10]);
+ ut_asserteq(22, acpi_test_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, acpi_test_get_length(ptr + 34));
+ ut_asserteq(1, ptr[37]);
+
+ /* Package with name and (integer) value */
+ ut_asserteq(PACKAGE_OP, ptr[38]);
+ ut_asserteq(15, acpi_test_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, acpi_test_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, acpi_test_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, acpi_test_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, acpi_test_get_length(pptr + 6));
+ ut_asserteq(2, pptr[9]);
+
+ /* UUID */
+ ut_asserteq(BUFFER_OP, pptr[10]);
+ ut_asserteq(22, acpi_test_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, acpi_test_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, acpi_test_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);
diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c
index 1cdbcf2131..a488db8b3e 100644
--- a/test/dm/acpigen.c
+++ b/test/dm/acpigen.c
@@ -18,6 +18,7 @@
#include <dm/test.h>
#include <dm/uclass-internal.h>
#include <test/ut.h>
+#include "acpi.h"
/* Maximum size of the ACPI context needed for most tests */
#define ACPI_CONTEXT_SIZE 150
@@ -31,7 +32,7 @@
#define TEST_INT32 0x12345678
#define TEST_INT64 0x4567890123456
-static int alloc_context_size(struct acpi_ctx **ctxp, int size)
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size)
{
struct acpi_ctx *ctx;
@@ -51,9 +52,17 @@ static int alloc_context_size(struct acpi_ctx **ctxp, int size)
return 0;
}
+int acpi_test_get_length(u8 *ptr)
+{
+ if (!(*ptr & 0x80))
+ return -EINVAL;
+
+ return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12;
+}
+
static int alloc_context(struct acpi_ctx **ctxp)
{
- return alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+ return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
}
static void free_context(struct acpi_ctx **ctxp)
@@ -357,20 +366,6 @@ static int dm_test_acpi_spi(struct unit_test_state *uts)
}
DM_TEST(dm_test_acpi_spi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-/**
- * 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 a length */
static int dm_test_acpi_len(struct unit_test_state *uts)
{
@@ -379,7 +374,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts)
u8 *ptr;
int i;
- ut_assertok(alloc_context_size(&ctx, size));
+ ut_assertok(acpi_test_alloc_context_size(&ctx, size));
ptr = acpigen_get_current(ctx);
@@ -387,7 +382,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts)
acpigen_write_len_f(ctx);
acpigen_emit_byte(ctx, 0x23);
acpigen_pop_len(ctx);
- ut_asserteq(1 + 3, get_length(ptr));
+ ut_asserteq(1 + 3, acpi_test_get_length(ptr));
/* Write 200 bytes so we need two length bytes */
ptr = ctx->current;
@@ -395,7 +390,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts)
for (i = 0; i < 200; i++)
acpigen_emit_byte(ctx, 0x23);
acpigen_pop_len(ctx);
- ut_asserteq(200 + 3, get_length(ptr));
+ ut_asserteq(200 + 3, acpi_test_get_length(ptr));
/* Write 40KB so we need three length bytes */
ptr = ctx->current;
@@ -403,7 +398,7 @@ static int dm_test_acpi_len(struct unit_test_state *uts)
for (i = 0; i < 40000; i++)
acpigen_emit_byte(ctx, 0x23);
acpigen_pop_len(ctx);
- ut_asserteq(40000 + 3, get_length(ptr));
+ ut_asserteq(40000 + 3, acpi_test_get_length(ptr));
free_context(&ctx);
@@ -429,7 +424,7 @@ static int dm_test_acpi_package(struct unit_test_state *uts)
acpigen_emit_byte(ctx, 0x23);
acpigen_pop_len(ctx);
ut_asserteq(PACKAGE_OP, ptr[0]);
- ut_asserteq(5, get_length(ptr + 1));
+ ut_asserteq(5, acpi_test_get_length(ptr + 1));
ut_asserteq(3, ptr[4]);
free_context(&ctx);
@@ -613,7 +608,7 @@ static int dm_test_acpi_uuid(struct unit_test_state *uts)
"dbb8e3e6-5886-4ba6-8795-1319f52a966b"));
ut_asserteq(23, acpigen_get_current(ctx) - ptr);
ut_asserteq(BUFFER_OP, ptr[0]);
- ut_asserteq(22, get_length(ptr + 1));
+ ut_asserteq(22, acpi_test_get_length(ptr + 1));
ut_asserteq(0xdbb8e3e6, get_unaligned((u32 *)(ptr + 7)));
ut_asserteq(0x5886, get_unaligned((u16 *)(ptr + 11)));
ut_asserteq(0x4ba6, get_unaligned((u16 *)(ptr + 13)));