0667900049
Some drivers in Linux support both device tree and ACPI. U-Boot itself uses Linux device-tree bindings for its own configuration but does not use ACPI. It is convenient to copy these values over to the ACPI DP table for passing to linux. Add some convenience functions to help with 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>
493 lines
13 KiB
C
493 lines
13 KiB
C
// 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);
|
|
|
|
/* Test emitting a GPIO */
|
|
static int dm_test_acpi_dp_gpio(struct unit_test_state *uts)
|
|
{
|
|
struct acpi_ctx *ctx;
|
|
struct acpi_dp *dp;
|
|
u8 *ptr, *pptr;
|
|
|
|
ut_assertok(alloc_context(&ctx));
|
|
|
|
dp = acpi_dp_new_table("FRED");
|
|
ut_assertnonnull(dp);
|
|
|
|
/* Try a few different parameters */
|
|
ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24,
|
|
ACPI_IRQ_ACTIVE_HIGH));
|
|
ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0,
|
|
ACPI_IRQ_ACTIVE_LOW));
|
|
|
|
ptr = acpigen_get_current(ctx);
|
|
ut_assertok(acpi_dp_write(ctx, dp));
|
|
ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr);
|
|
|
|
pptr = ptr + 0x2c; //0x3a;
|
|
ut_asserteq_str("reset", (char *)pptr);
|
|
ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
|
|
ut_asserteq(0x23, pptr[0x1b]);
|
|
ut_asserteq(0x24, pptr[0x1d]);
|
|
ut_asserteq(ZERO_OP, pptr[0x1e]);
|
|
|
|
pptr = ptr + 0x51;
|
|
ut_asserteq_str("allow", (char *)pptr);
|
|
ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
|
|
ut_asserteq(ZERO_OP, pptr[0x1a]);
|
|
ut_asserteq(ZERO_OP, pptr[0x1b]);
|
|
ut_asserteq(ONE_OP, pptr[0x1c]);
|
|
|
|
return 0;
|
|
}
|
|
DM_TEST(dm_test_acpi_dp_gpio, 0);
|
|
|
|
/* Test copying info from the device tree to ACPI tables */
|
|
static int dm_test_acpi_dp_copy(struct unit_test_state *uts)
|
|
{
|
|
struct acpi_ctx *ctx;
|
|
struct udevice *dev;
|
|
struct acpi_dp *dp;
|
|
ofnode node;
|
|
u8 *ptr;
|
|
|
|
ut_assertok(alloc_context(&ctx));
|
|
|
|
dp = acpi_dp_new_table("FRED");
|
|
ut_assertnonnull(dp);
|
|
|
|
ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
|
|
ut_asserteq_str("a-test", dev->name);
|
|
|
|
ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value"));
|
|
ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value"));
|
|
ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value"));
|
|
|
|
ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value"));
|
|
ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value"));
|
|
|
|
node = ofnode_path("/chosen");
|
|
ut_assert(ofnode_valid(node));
|
|
ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values"));
|
|
ut_asserteq(-EINVAL,
|
|
acpi_dp_ofnode_copy_int(node, dp, "missing-value"));
|
|
|
|
ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting"));
|
|
ut_asserteq(-EINVAL,
|
|
acpi_dp_ofnode_copy_str(node, dp, "missing-value"));
|
|
|
|
ptr = acpigen_get_current(ctx);
|
|
ut_assertok(acpi_dp_write(ctx, dp));
|
|
ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr);
|
|
|
|
ut_asserteq(STRING_PREFIX, ptr[0x2b]);
|
|
ut_asserteq_str("int-value", (char *)ptr + 0x2c);
|
|
ut_asserteq(WORD_PREFIX, ptr[0x36]);
|
|
ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37)));
|
|
|
|
ut_asserteq(STRING_PREFIX, ptr[0x3e]);
|
|
ut_asserteq_str("uint-value", (char *)ptr + 0x3f);
|
|
ut_asserteq(DWORD_PREFIX, ptr[0x4a]);
|
|
ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b)));
|
|
|
|
ut_asserteq(STRING_PREFIX, ptr[0x54]);
|
|
ut_asserteq_str("str-value", (char *)ptr + 0x55);
|
|
ut_asserteq(STRING_PREFIX, ptr[0x5f]);
|
|
ut_asserteq_str("test string", (char *)ptr + 0x60);
|
|
|
|
ut_asserteq(STRING_PREFIX, ptr[0x71]);
|
|
ut_asserteq_str("int-values", (char *)ptr + 0x72);
|
|
ut_asserteq(WORD_PREFIX, ptr[0x7d]);
|
|
ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e)));
|
|
|
|
ut_asserteq(STRING_PREFIX, ptr[0x85]);
|
|
ut_asserteq_str("setting", (char *)ptr + 0x86);
|
|
ut_asserteq(STRING_PREFIX, ptr[0x8e]);
|
|
ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f));
|
|
|
|
return 0;
|
|
}
|
|
DM_TEST(dm_test_acpi_dp_copy, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
|