Message ID | 20200613205459.v3.13.If6772cfcfc5b88c3cbfb77bb384676e74ddcd985@changeid |
---|---|
State | Accepted |
Commit | 7e148f2ed365c89f50701ed45acd6e36138de447 |
Headers | show |
Series | dm: Add programmatic generation of ACPI tables (part B) | expand |
On Sun, Jun 14, 2020 at 10:55 AM Simon Glass <sjg at chromium.org> wrote: > > It is convenient to write a length value for preceding a block of data. > Of course the length is not known or is hard to calculate a priori. So add > a way to mark the start on a stack, so the length can be updated when > known. > > Signed-off-by: Simon Glass <sjg at chromium.org> > Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com> > --- > > Changes in v3: > - Add a reference to the ACPI spec > - Add a define for the 0x80 constant > - Move the function comments into this patch > > include/acpi/acpigen.h | 38 +++++++++++++++++++++++++ > include/dm/acpi.h | 7 +++++ > lib/acpi/acpigen.c | 33 ++++++++++++++++++++++ > test/dm/acpigen.c | 64 ++++++++++++++++++++++++++++++++++++++++-- > 4 files changed, 140 insertions(+), 2 deletions(-) > > diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h > index 7365cce738..12cd1bd578 100644 > --- a/include/acpi/acpigen.h > +++ b/include/acpi/acpigen.h > @@ -14,6 +14,9 @@ > > struct acpi_ctx; > > +/* Top 4 bits of the value used to indicate a three-byte length value */ > +#define ACPI_PKG_LEN_3_BYTES 0x80 > + > /** > * acpigen_get_current() - Get the current ACPI code output pointer > * > @@ -65,4 +68,39 @@ void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size); > */ > void acpigen_emit_string(struct acpi_ctx *ctx, const char *str); > > +/** > + * acpigen_write_len_f() - Write a 'forward' length placeholder > + * > + * This adds space for a length value in the ACPI stream and pushes the current > + * position (before the length) on the stack. After calling this you can write > + * some data and then call acpigen_pop_len() to update the length value. > + * > + * Usage: > + * > + * acpigen_write_len_f() ------\ > + * acpigen_write...() | > + * acpigen_write...() | > + * acpigen_write_len_f() --\ | > + * acpigen_write...() | | > + * acpigen_write...() | | > + * acpigen_pop_len() ------/ | > + * acpigen_write...() | > + * acpigen_pop_len() ----------/ > + * > + * See ACPI 6.3 section 20.2.4 Package Length Encoding It's better we put some more comments here mentioning that we are hardcoding a 3 byte packet length encoding that can represent up to 2*20 packet length in total. Any chance to optimize this routine not to use a hardcode 3 byte encoding? > + * > + * @ctx: ACPI context pointer > + */ > +void acpigen_write_len_f(struct acpi_ctx *ctx); > + > +/** > + * acpigen_pop_len() - Update the previously stacked length placeholder > + * > + * Call this after the data for the block gas been written. It updates the typo: gas -> has > + * top length value in the stack and pops it off. > + * > + * @ctx: ACPI context pointer > + */ > +void acpigen_pop_len(struct acpi_ctx *ctx); > + > #endif > diff --git a/include/dm/acpi.h b/include/dm/acpi.h > index 696b1a96a0..f27ca64507 100644 > --- a/include/dm/acpi.h > +++ b/include/dm/acpi.h > @@ -22,6 +22,9 @@ > /* Length of an ACPI name string including nul terminator */ nits: nul -> null > #define ACPI_NAME_MAX (ACPI_NAME_LEN + 1) > > +/* Number of nested objects supported */ > +#define ACPIGEN_LENSTACK_SIZE 10 > + > #if !defined(__ACPI__) > > /** > @@ -35,6 +38,8 @@ > * adding a new table. The RSDP holds pointers to the RSDT and XSDT. > * @rsdt: Pointer to the Root System Description Table > * @xsdt: Pointer to the Extended System Description Table > + * @len_stack: Stack of 'length' words to fix up later > + * @ltop: Points to current top of stack (0 = empty) > */ > struct acpi_ctx { > void *base; > @@ -42,6 +47,8 @@ struct acpi_ctx { > struct acpi_rsdp *rsdp; > struct acpi_rsdt *rsdt; > struct acpi_xsdt *xsdt; > + char *len_stack[ACPIGEN_LENSTACK_SIZE]; > + int ltop; > }; > > /** > diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c > index 1223f0d1c4..2c2b604d80 100644 > --- a/lib/acpi/acpigen.c > +++ b/lib/acpi/acpigen.c > @@ -10,6 +10,7 @@ > > #include <common.h> > #include <dm.h> > +#include <log.h> > #include <acpi/acpigen.h> > #include <dm/acpi.h> > > @@ -37,6 +38,38 @@ void acpigen_emit_dword(struct acpi_ctx *ctx, uint data) > acpigen_emit_byte(ctx, (data >> 24) & 0xff); > } > > +/* > + * Maximum length for an ACPI object generated by this code, > + * > + * If you need to change this, change acpigen_write_len_f(ctx) and > + * acpigen_pop_len(ctx) > + */ > +#define ACPIGEN_MAXLEN 0xfffff > + > +void acpigen_write_len_f(struct acpi_ctx *ctx) > +{ > + assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1)); > + ctx->len_stack[ctx->ltop++] = ctx->current; > + acpigen_emit_byte(ctx, 0); > + acpigen_emit_byte(ctx, 0); > + acpigen_emit_byte(ctx, 0); > +} > + > +void acpigen_pop_len(struct acpi_ctx *ctx) > +{ > + int len; > + char *p; > + > + assert(ctx->ltop > 0); > + p = ctx->len_stack[--ctx->ltop]; > + len = ctx->current - (void *)p; > + assert(len <= ACPIGEN_MAXLEN); > + /* generate store length for 0xfffff max */ > + p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf); > + p[1] = len >> 4 & 0xff; > + p[2] = len >> 12 & 0xff; > +} > + > void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) > { > int i; > diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c > index 571510722d..71f70ebf71 100644 > --- a/test/dm/acpigen.c > +++ b/test/dm/acpigen.c > @@ -22,7 +22,7 @@ > #define TEST_STRING "frogmore" > #define TEST_STREAM2 "\xfa\xde" > > -static int alloc_context(struct acpi_ctx **ctxp) > +static int alloc_context_size(struct acpi_ctx **ctxp, int size) > { > struct acpi_ctx *ctx; > > @@ -30,17 +30,23 @@ static int alloc_context(struct acpi_ctx **ctxp) > ctx = malloc(sizeof(*ctx)); > if (!ctx) > return -ENOMEM; > - ctx->base = malloc(150); > + ctx->base = malloc(size); > if (!ctx->base) { > free(ctx); > return -ENOMEM; > } > + ctx->ltop = 0; > ctx->current = ctx->base; > *ctxp = ctx; > > return 0; > } > > +static int alloc_context(struct acpi_ctx **ctxp) > +{ > + return alloc_context_size(ctxp, 150); > +} > + > static void free_context(struct acpi_ctx **ctxp) > { > free((*ctxp)->base); > @@ -338,3 +344,57 @@ static int dm_test_acpi_spi(struct unit_test_state *uts) > return 0; > } > 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) > +{ > + const int size = 0xc0000; > + struct acpi_ctx *ctx; > + u8 *ptr; > + int i; > + > + ut_assertok(alloc_context_size(&ctx, size)); > + > + ptr = acpigen_get_current(ctx); > + > + /* Write a byte and a 3-byte length */ > + acpigen_write_len_f(ctx); > + acpigen_emit_byte(ctx, 0x23); > + acpigen_pop_len(ctx); > + ut_asserteq(1 + 3, get_length(ptr)); > + > + /* Write 200 bytes so we need two length bytes */ > + ptr = ctx->current; > + acpigen_write_len_f(ctx); > + for (i = 0; i < 200; i++) > + acpigen_emit_byte(ctx, 0x23); > + acpigen_pop_len(ctx); > + ut_asserteq(200 + 3, get_length(ptr)); > + > + /* Write 40KB so we need three length bytes */ > + ptr = ctx->current; > + acpigen_write_len_f(ctx); > + for (i = 0; i < 40000; i++) > + acpigen_emit_byte(ctx, 0x23); > + acpigen_pop_len(ctx); > + ut_asserteq(40000 + 3, get_length(ptr)); > + > + free_context(&ctx); > + > + return 0; > +} > +DM_TEST(dm_test_acpi_len, 0); Regards, Bin
diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 7365cce738..12cd1bd578 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -14,6 +14,9 @@ struct acpi_ctx; +/* Top 4 bits of the value used to indicate a three-byte length value */ +#define ACPI_PKG_LEN_3_BYTES 0x80 + /** * acpigen_get_current() - Get the current ACPI code output pointer * @@ -65,4 +68,39 @@ void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size); */ void acpigen_emit_string(struct acpi_ctx *ctx, const char *str); +/** + * acpigen_write_len_f() - Write a 'forward' length placeholder + * + * This adds space for a length value in the ACPI stream and pushes the current + * position (before the length) on the stack. After calling this you can write + * some data and then call acpigen_pop_len() to update the length value. + * + * Usage: + * + * acpigen_write_len_f() ------\ + * acpigen_write...() | + * acpigen_write...() | + * acpigen_write_len_f() --\ | + * acpigen_write...() | | + * acpigen_write...() | | + * acpigen_pop_len() ------/ | + * acpigen_write...() | + * acpigen_pop_len() ----------/ + * + * See ACPI 6.3 section 20.2.4 Package Length Encoding + * + * @ctx: ACPI context pointer + */ +void acpigen_write_len_f(struct acpi_ctx *ctx); + +/** + * acpigen_pop_len() - Update the previously stacked length placeholder + * + * Call this after the data for the block gas been written. It updates the + * top length value in the stack and pops it off. + * + * @ctx: ACPI context pointer + */ +void acpigen_pop_len(struct acpi_ctx *ctx); + #endif diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 696b1a96a0..f27ca64507 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -22,6 +22,9 @@ /* Length of an ACPI name string including nul terminator */ #define ACPI_NAME_MAX (ACPI_NAME_LEN + 1) +/* Number of nested objects supported */ +#define ACPIGEN_LENSTACK_SIZE 10 + #if !defined(__ACPI__) /** @@ -35,6 +38,8 @@ * adding a new table. The RSDP holds pointers to the RSDT and XSDT. * @rsdt: Pointer to the Root System Description Table * @xsdt: Pointer to the Extended System Description Table + * @len_stack: Stack of 'length' words to fix up later + * @ltop: Points to current top of stack (0 = empty) */ struct acpi_ctx { void *base; @@ -42,6 +47,8 @@ struct acpi_ctx { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; + char *len_stack[ACPIGEN_LENSTACK_SIZE]; + int ltop; }; /** diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 1223f0d1c4..2c2b604d80 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -10,6 +10,7 @@ #include <common.h> #include <dm.h> +#include <log.h> #include <acpi/acpigen.h> #include <dm/acpi.h> @@ -37,6 +38,38 @@ void acpigen_emit_dword(struct acpi_ctx *ctx, uint data) acpigen_emit_byte(ctx, (data >> 24) & 0xff); } +/* + * Maximum length for an ACPI object generated by this code, + * + * If you need to change this, change acpigen_write_len_f(ctx) and + * acpigen_pop_len(ctx) + */ +#define ACPIGEN_MAXLEN 0xfffff + +void acpigen_write_len_f(struct acpi_ctx *ctx) +{ + assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1)); + ctx->len_stack[ctx->ltop++] = ctx->current; + acpigen_emit_byte(ctx, 0); + acpigen_emit_byte(ctx, 0); + acpigen_emit_byte(ctx, 0); +} + +void acpigen_pop_len(struct acpi_ctx *ctx) +{ + int len; + char *p; + + assert(ctx->ltop > 0); + p = ctx->len_stack[--ctx->ltop]; + len = ctx->current - (void *)p; + assert(len <= ACPIGEN_MAXLEN); + /* generate store length for 0xfffff max */ + p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf); + p[1] = len >> 4 & 0xff; + p[2] = len >> 12 & 0xff; +} + void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size) { int i; diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 571510722d..71f70ebf71 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -22,7 +22,7 @@ #define TEST_STRING "frogmore" #define TEST_STREAM2 "\xfa\xde" -static int alloc_context(struct acpi_ctx **ctxp) +static int alloc_context_size(struct acpi_ctx **ctxp, int size) { struct acpi_ctx *ctx; @@ -30,17 +30,23 @@ static int alloc_context(struct acpi_ctx **ctxp) ctx = malloc(sizeof(*ctx)); if (!ctx) return -ENOMEM; - ctx->base = malloc(150); + ctx->base = malloc(size); if (!ctx->base) { free(ctx); return -ENOMEM; } + ctx->ltop = 0; ctx->current = ctx->base; *ctxp = ctx; return 0; } +static int alloc_context(struct acpi_ctx **ctxp) +{ + return alloc_context_size(ctxp, 150); +} + static void free_context(struct acpi_ctx **ctxp) { free((*ctxp)->base); @@ -338,3 +344,57 @@ static int dm_test_acpi_spi(struct unit_test_state *uts) return 0; } 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) +{ + const int size = 0xc0000; + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context_size(&ctx, size)); + + ptr = acpigen_get_current(ctx); + + /* Write a byte and a 3-byte length */ + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(1 + 3, get_length(ptr)); + + /* Write 200 bytes so we need two length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 200; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(200 + 3, get_length(ptr)); + + /* Write 40KB so we need three length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 40000; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(40000 + 3, get_length(ptr)); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_len, 0);