board: ge: make VPD code common
authorMartyn Welch <martyn.welch@collabora.co.uk>
Wed, 8 Nov 2017 15:35:15 +0000 (15:35 +0000)
committerStefano Babic <sbabic@denx.de>
Mon, 20 Nov 2017 08:58:31 +0000 (09:58 +0100)
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>
Acked-by: Stefano Babic <sbabic@denx.de>
board/ge/bx50v3/Makefile
board/ge/bx50v3/bx50v3.c
board/ge/bx50v3/vpd_reader.c [deleted file]
board/ge/bx50v3/vpd_reader.h [deleted file]
board/ge/common/Makefile [new file with mode: 0644]
board/ge/common/vpd_reader.c [new file with mode: 0644]
board/ge/common/vpd_reader.h [new file with mode: 0644]

index 2fff27bc77529c754d768d39eed457151e982415..bcd149f5b065a0be0d7746f5af982b6e4a4adb41 100644 (file)
@@ -5,4 +5,4 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 
-obj-y  := bx50v3.o vpd_reader.o
+obj-y  := bx50v3.o
index 2e8f394eaf95bebd95a25a53efc10e31d69e9a4d..37de9901767ab9503accdb7948a138132b22c80e 100644 (file)
@@ -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 (file)
index 98da893..0000000
+++ /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 (file)
index efa172a..0000000
+++ /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 (file)
index 0000000..93e6c01
--- /dev/null
@@ -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 (file)
index 0000000..7367427
--- /dev/null
@@ -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 (file)
index 0000000..4abba8f
--- /dev/null
@@ -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));