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

Stefano Babic sbabic at denx.de
Thu Nov 9 09:27:34 UTC 2017


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


More information about the U-Boot mailing list