u-boot-brain/test/dm/acpi_dp.c
Simon Glass 0e5a0a00d6 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>
2020-07-17 14:32:24 +08:00

386 lines
9.9 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);