[U-Boot] [PATCH v5 7/8] board: ge: make VPD code common

Stefano Babic
2017-11-09T09:27:34Z

On 08/11/2017 16:35, Martyn Welch wrote:
> The VPD data is used on a number of GE products. Move the parsing code to
> a common location so that we can share this code.
>
> Signed-off-by: Martyn Welch <martyn.welch@collabora.co.uk>
> ---
> Changes in v4:
> - New patch.
>
> Changes in v5:
> - Fixed checkpatch issues.
>
> board/ge/bx50v3/Makefile | 2 +-
> board/ge/bx50v3/bx50v3.c | 2 +-
> board/ge/bx50v3/vpd_reader.c | 228 -------------------------------------------
> board/ge/bx50v3/vpd_reader.h | 25 -----
> board/ge/common/Makefile | 7 ++
> board/ge/common/vpd_reader.c | 197 +++++++++++++++++++++++++++++++++++++
> board/ge/common/vpd_reader.h | 17 ++++
> 7 files changed, 223 insertions(+), 255 deletions(-)
> delete mode 100644 board/ge/bx50v3/vpd_reader.c
> delete mode 100644 board/ge/bx50v3/vpd_reader.h
> create mode 100644 board/ge/common/Makefile
> create mode 100644 board/ge/common/vpd_reader.c
> create mode 100644 board/ge/common/vpd_reader.h
>
> diff --git a/board/ge/bx50v3/Makefile b/board/ge/bx50v3/Makefile
> index 2fff27b..bcd149f 100644
> --- a/board/ge/bx50v3/Makefile
> +++ b/board/ge/bx50v3/Makefile
> @@ -5,4 +5,4 @@
> # SPDX-License-Identifier: GPL-2.0+
> #
>
> -obj-y := bx50v3.o vpd_reader.o
> +obj-y := bx50v3.o
> diff --git a/board/ge/bx50v3/bx50v3.c b/board/ge/bx50v3/bx50v3.c
> index 2e8f394..37de990 100644
> --- a/board/ge/bx50v3/bx50v3.c
> +++ b/board/ge/bx50v3/bx50v3.c
> @@ -28,7 +28,7 @@
> #include <input.h>
> #include <pwm.h>
> #include <stdlib.h>
> -#include "vpd_reader.h"
> +#include "../common/vpd_reader.h"
> DECLARE_GLOBAL_DATA_PTR;
>
> #ifndef CONFIG_SYS_I2C_EEPROM_ADDR
> diff --git a/board/ge/bx50v3/vpd_reader.c b/board/ge/bx50v3/vpd_reader.c
> deleted file mode 100644
> index 98da893..0000000
> --- a/board/ge/bx50v3/vpd_reader.c
> +++ /dev/null
> @@ -1,228 +0,0 @@
> -/*
> - * Copyright 2016 General Electric Company
> - *
> - * SPDX-License-Identifier: GPL-2.0+
> - */
> -
> -#include "vpd_reader.h"
> -
> -#include <linux/bch.h>
> -#include <stdlib.h>
> -
> -
> -/* BCH configuration */
> -
> -const struct {
> - int header_ecc_capability_bits;
> - int data_ecc_capability_bits;
> - unsigned int prim_poly;
> - struct {
> - int min;
> - int max;
> - } galois_field_order;
> -} bch_configuration = {
> - .header_ecc_capability_bits = 4,
> - .data_ecc_capability_bits = 16,
> - .prim_poly = 0,
> - .galois_field_order = {
> - .min = 5,
> - .max = 15,
> - },
> -};
> -
> -static int calculate_galois_field_order(size_t source_length)
> -{
> - int gfo = bch_configuration.galois_field_order.min;
> -
> - for (; gfo < bch_configuration.galois_field_order.max &&
> - ((((1 << gfo) - 1) - ((int)source_length * 8)) < 0);
> - gfo++) {
> - }
> -
> - if (gfo == bch_configuration.galois_field_order.max) {
> - return -1;
> - }
> -
> - return gfo + 1;
> -}
> -
> -static int verify_bch(int ecc_bits, unsigned int prim_poly,
> - uint8_t * data, size_t data_length,
> - const uint8_t * ecc, size_t ecc_length)
> -{
> - int gfo = calculate_galois_field_order(data_length);
> - if (gfo < 0) {
> - return -1;
> - }
> -
> - struct bch_control * bch = init_bch(gfo, ecc_bits, prim_poly);
> - if (!bch) {
> - return -1;
> - }
> -
> - if (bch->ecc_bytes != ecc_length) {
> - free_bch(bch);
> - return -1;
> - }
> -
> - unsigned * errloc = (unsigned *)calloc(data_length, sizeof(unsigned));
> - int errors = decode_bch(
> - bch, data, data_length, ecc, NULL, NULL, errloc);
> - free_bch(bch);
> - if (errors < 0) {
> - free(errloc);
> - return -1;
> - }
> -
> - if (errors > 0) {
> - for (int n = 0; n < errors; n++) {
> - if (errloc[n] >= 8 * data_length) {
> - /* n-th error located in ecc (no need for data correction) */
> - } else {
> - /* n-th error located in data */
> - data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
> - }
> - }
> - }
> -
> - free(errloc);
> - return 0;
> -}
> -
> -
> -static const int ID = 0;
> -static const int LEN = 1;
> -static const int VER = 2;
> -static const int TYP = 3;
> -static const int BLOCK_SIZE = 4;
> -
> -static const uint8_t HEADER_BLOCK_ID = 0x00;
> -static const uint8_t HEADER_BLOCK_LEN = 18;
> -static const uint32_t HEADER_BLOCK_MAGIC = 0xca53ca53;
> -static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
> -static const size_t HEADER_BLOCK_ECC_OFF = 14;
> -static const size_t HEADER_BLOCK_ECC_LEN = 4;
> -
> -static const uint8_t ECC_BLOCK_ID = 0xFF;
> -
> -int vpd_reader(
> - size_t size,
> - uint8_t * data,
> - void * userdata,
> - int (*fn)(
> - void * userdata,
> - uint8_t id,
> - uint8_t version,
> - uint8_t type,
> - size_t size,
> - uint8_t const * data))
> -{
> - if ( size < HEADER_BLOCK_LEN
> - || data == NULL
> - || fn == NULL) {
> - return -EINVAL;
> - }
> -
> - /*
> - * +--------------------+--------------------+--//--+--------------------+
> - * | header block | data block | ... | ecc block |
> - * +--------------------+--------------------+--//--+--------------------+
> - * : : :
> - * +------+-------+-----+ +------+-------------+
> - * | id | magic | ecc | | ... | ecc |
> - * | len | off | | +------+-------------+
> - * | ver | size | | :
> - * | type | | | :
> - * +------+-------+-----+ :
> - * : : : :
> - * <----- [1] ----> <----------- [2] ----------->
> - *
> - * Repair (if necessary) the contents of header block [1] by using a
> - * 4 byte ECC located at the end of the header block. A successful
> - * return value means that we can trust the header.
> - */
> - int ret = verify_bch(
> - bch_configuration.header_ecc_capability_bits,
> - bch_configuration.prim_poly,
> - data,
> - HEADER_BLOCK_VERIFY_LEN,
> - &data[HEADER_BLOCK_ECC_OFF],
> - HEADER_BLOCK_ECC_LEN);
> - if (ret < 0) {
> - return ret;
> - }
> -
> - /* Validate header block { id, length, version, type }. */
> - if ( data[ID] != HEADER_BLOCK_ID
> - || data[LEN] != HEADER_BLOCK_LEN
> - || data[VER] != 0
> - || data[TYP] != 0
> - || ntohl(*(uint32_t *)(&data[4])) != HEADER_BLOCK_MAGIC) {
> - return -EINVAL;
> - }
> -
> - uint32_t offset = ntohl(*(uint32_t *)(&data[8]));
> - uint16_t size_bits = ntohs(*(uint16_t *)(&data[12]));
> -
> - /* Check that ECC header fits. */
> - if (offset + 3 >= size) {
> - return -EINVAL;
> - }
> -
> - /* Validate ECC block. */
> - uint8_t * ecc = &data[offset];
> - if ( ecc[ID] != ECC_BLOCK_ID
> - || ecc[LEN] < BLOCK_SIZE
> - || ecc[LEN] + offset > size
> - || ecc[LEN] - BLOCK_SIZE != size_bits / 8
> - || ecc[VER] != 1
> - || ecc[TYP] != 1) {
> - return -EINVAL;
> - }
> -
> - /*
> - * Use the header block to locate the ECC block and verify the data
> - * blocks [2] against the ecc block ECC.
> - */
> - ret = verify_bch(
> - bch_configuration.data_ecc_capability_bits,
> - bch_configuration.prim_poly,
> - &data[data[LEN]],
> - offset - data[LEN],
> - &data[offset + BLOCK_SIZE],
> - ecc[LEN] - BLOCK_SIZE);
> - if (ret < 0) {
> - return ret;
> - }
> -
> - /* Stop after ECC. Ignore possible zero padding. */
> - size = offset;
> -
> - for (;;) {
> - /* Move to next block. */
> - size -= data[LEN];
> - data += data[LEN];
> -
> - if (size == 0) {
> - /* Finished iterating through blocks. */
> - return 0;
> - }
> -
> - if ( size < BLOCK_SIZE
> - || data[LEN] < BLOCK_SIZE) {
> - /* Not enough data for a header, or short header. */
> - return -EINVAL;
> - }
> -
> - ret = fn(
> - userdata,
> - data[ID],
> - data[VER],
> - data[TYP],
> - data[LEN] - BLOCK_SIZE,
> - &data[BLOCK_SIZE]);
> - if (ret) {
> - return ret;
> - }
> - }
> -}
> diff --git a/board/ge/bx50v3/vpd_reader.h b/board/ge/bx50v3/vpd_reader.h
> deleted file mode 100644
> index efa172a..0000000
> --- a/board/ge/bx50v3/vpd_reader.h
> +++ /dev/null
> @@ -1,25 +0,0 @@
> -/*
> - * Copyright 2016 General Electric Company
> - *
> - * SPDX-License-Identifier: GPL-2.0+
> - */
> -
> -#include "common.h"
> -
> -/*
> - * Read VPD from given data, verify content, and call callback
> - * for each vital product data block.
> - *
> - * Returns Non-zero on error. Negative numbers encode errno.
> - */
> -int vpd_reader(
> - size_t size,
> - uint8_t * data,
> - void * userdata,
> - int (*fn)(
> - void * userdata,
> - uint8_t id,
> - uint8_t version,
> - uint8_t type,
> - size_t size,
> - uint8_t const * data));
> diff --git a/board/ge/common/Makefile b/board/ge/common/Makefile
> new file mode 100644
> index 0000000..93e6c01
> --- /dev/null
> +++ b/board/ge/common/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Copyright 2017 General Electric Company
> +#
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +
> +obj-y := vpd_reader.o
> diff --git a/board/ge/common/vpd_reader.c b/board/ge/common/vpd_reader.c
> new file mode 100644
> index 0000000..7367427
> --- /dev/null
> +++ b/board/ge/common/vpd_reader.c
> @@ -0,0 +1,197 @@
> +/*
> + * Copyright 2016 General Electric Company
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include "vpd_reader.h"
> +
> +#include <linux/bch.h>
> +#include <stdlib.h>
> +
> +/* BCH configuration */
> +
> +const struct {
> + int header_ecc_capability_bits;
> + int data_ecc_capability_bits;
> + unsigned int prim_poly;
> + struct {
> + int min;
> + int max;
> + } galois_field_order;
> +} bch_configuration = {
> + .header_ecc_capability_bits = 4,
> + .data_ecc_capability_bits = 16,
> + .prim_poly = 0,
> + .galois_field_order = {
> + .min = 5,
> + .max = 15,
> + },
> +};
> +
> +static int calculate_galois_field_order(size_t source_length)
> +{
> + int gfo = bch_configuration.galois_field_order.min;
> +
> + for (; gfo < bch_configuration.galois_field_order.max &&
> + ((((1 << gfo) - 1) - ((int)source_length * 8)) < 0);
> + gfo++) {
> + }
> +
> + if (gfo == bch_configuration.galois_field_order.max)
> + return -1;
> +
> + return gfo + 1;
> +}
> +
> +static int verify_bch(int ecc_bits, unsigned int prim_poly, u8 *data,
> + size_t data_length, const u8 *ecc, size_t ecc_length)
> +{
> + int gfo = calculate_galois_field_order(data_length);
> +
> + if (gfo < 0)
> + return -1;
> +
> + struct bch_control *bch = init_bch(gfo, ecc_bits, prim_poly);
> +
> + if (!bch)
> + return -1;
> +
> + if (bch->ecc_bytes != ecc_length) {
> + free_bch(bch);
> + return -1;
> + }
> +
> + unsigned int *errloc = (unsigned int *)calloc(data_length,
> + sizeof(unsigned int));
> + int errors = decode_bch(bch, data, data_length, ecc, NULL, NULL,
> + errloc);
> +
> + free_bch(bch);
> + if (errors < 0) {
> + free(errloc);
> + return -1;
> + }
> +
> + if (errors > 0) {
> + for (int n = 0; n < errors; n++) {
> + if (errloc[n] >= 8 * data_length) {
> + /*
> + * n-th error located in ecc (no need for data
> + * correction)
> + */
> + } else {
> + /* n-th error located in data */
> + data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
> + }
> + }
> + }
> +
> + free(errloc);
> + return 0;
> +}
> +
> +static const int ID;
> +static const int LEN = 1;
> +static const int VER = 2;
> +static const int TYP = 3;
> +static const int BLOCK_SIZE = 4;
> +
> +static const u8 HEADER_BLOCK_ID;
> +static const u8 HEADER_BLOCK_LEN = 18;
> +static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53;
> +static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
> +static const size_t HEADER_BLOCK_ECC_OFF = 14;
> +static const size_t HEADER_BLOCK_ECC_LEN = 4;
> +
> +static const u8 ECC_BLOCK_ID = 0xFF;
> +
> +int vpd_reader(size_t size, u8 *data, void *userdata,
> + int (*fn)(void *userdata, u8 id, u8 version, u8 type,
> + size_t size, u8 const *data))
> +{
> + if (size < HEADER_BLOCK_LEN || !data || !fn)
> + return -EINVAL;
> +
> + /*
> + * +--------------------+----------------+--//--+--------------------+
> + * | header block | data block | ... | ecc block |
> + * +--------------------+----------------+--//--+--------------------+
> + * : : :
> + * +------+-------+-----+ +------+-------------+
> + * | id | magic | ecc | | ... | ecc |
> + * | len | off | | +------+-------------+
> + * | ver | size | | :
> + * | type | | | :
> + * +------+-------+-----+ :
> + * : : : :
> + * <----- [1] ----> <--------- [2] --------->
> + *
> + * Repair (if necessary) the contents of header block [1] by using a
> + * 4 byte ECC located at the end of the header block. A successful
> + * return value means that we can trust the header.
> + */
> + int ret = verify_bch(bch_configuration.header_ecc_capability_bits,
> + bch_configuration.prim_poly, data,
> + HEADER_BLOCK_VERIFY_LEN,
> + &data[HEADER_BLOCK_ECC_OFF], HEADER_BLOCK_ECC_LEN);
> + if (ret < 0)
> + return ret;
> +
> + /* Validate header block { id, length, version, type }. */
> + if (data[ID] != HEADER_BLOCK_ID || data[LEN] != HEADER_BLOCK_LEN ||
> + data[VER] != 0 || data[TYP] != 0 ||
> + ntohl(*(u32 *)(&data[4])) != HEADER_BLOCK_MAGIC)
> + return -EINVAL;
> +
> + u32 offset = ntohl(*(u32 *)(&data[8]));
> + u16 size_bits = ntohs(*(u16 *)(&data[12]));
> +
> + /* Check that ECC header fits. */
> + if (offset + 3 >= size)
> + return -EINVAL;
> +
> + /* Validate ECC block. */
> + u8 *ecc = &data[offset];
> +
> + if (ecc[ID] != ECC_BLOCK_ID || ecc[LEN] < BLOCK_SIZE ||
> + ecc[LEN] + offset > size ||
> + ecc[LEN] - BLOCK_SIZE != size_bits / 8 || ecc[VER] != 1 ||
> + ecc[TYP] != 1)
> + return -EINVAL;
> +
> + /*
> + * Use the header block to locate the ECC block and verify the data
> + * blocks [2] against the ecc block ECC.
> + */
> + ret = verify_bch(bch_configuration.data_ecc_capability_bits,
> + bch_configuration.prim_poly, &data[data[LEN]],
> + offset - data[LEN], &data[offset + BLOCK_SIZE],
> + ecc[LEN] - BLOCK_SIZE);
> + if (ret < 0)
> + return ret;
> +
> + /* Stop after ECC. Ignore possible zero padding. */
> + size = offset;
> +
> + for (;;) {
> + /* Move to next block. */
> + size -= data[LEN];
> + data += data[LEN];
> +
> + if (size == 0) {
> + /* Finished iterating through blocks. */
> + return 0;
> + }
> +
> + if (size < BLOCK_SIZE || data[LEN] < BLOCK_SIZE) {
> + /* Not enough data for a header, or short header. */
> + return -EINVAL;
> + }
> +
> + ret = fn(userdata, data[ID], data[VER], data[TYP],
> + data[LEN] - BLOCK_SIZE, &data[BLOCK_SIZE]);
> + if (ret)
> + return ret;
> + }
> +}
> diff --git a/board/ge/common/vpd_reader.h b/board/ge/common/vpd_reader.h
> new file mode 100644
> index 0000000..4abba8f
> --- /dev/null
> +++ b/board/ge/common/vpd_reader.h
> @@ -0,0 +1,17 @@
> +/*
> + * Copyright 2016 General Electric Company
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include "common.h"
> +
> +/*
> + * Read VPD from given data, verify content, and call callback
> + * for each vital product data block.
> + *
> + * Returns Non-zero on error. Negative numbers encode errno.
> + */
> +int vpd_reader(size_t size, u8 *data, void *userdata,
> + int (*fn)(void *userdata, u8 id, u8 version, u8 type,
> + size_t size, u8 const *data));
>

Acked-by: Stefano Babic <sbabic@denx.de>

Best regards,
Stefano Babic

--
=====================================================================
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic at denx.de
=====================================================================