[PATCH v3 19/35] acpi: Support writing Device Properties objects via _DSD

Bin Meng bmeng.cn at gmail.com
Mon Jun 29 04:07:20 CEST 2020


Hi Simon,

On Sun, Jun 14, 2020 at 10:55 AM Simon Glass <sjg at 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 at chromium.org>
> Reviewed-by: Wolfgang Wallner <wolfgang.wallner at 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

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 at 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


More information about the U-Boot mailing list