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.we...@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 <sba...@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: sba...@denx.de ===================================================================== _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot