aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ecc.h53
-rw-r--r--libflash/Makefile.inc2
-rw-r--r--libflash/ecc.c175
3 files changed, 229 insertions, 1 deletions
diff --git a/include/ecc.h b/include/ecc.h
new file mode 100644
index 0000000..d5c30cb
--- /dev/null
+++ b/include/ecc.h
@@ -0,0 +1,53 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is based on the hostboot ecc code */
+
+#ifndef __ECC_H
+#define __ECC_H
+
+#include <stdint.h>
+#include <skiboot.h>
+
+/* Bit field identifiers for syndrome calculations. */
+enum eccbitfields
+{
+ GD = 0xff, //< Good, ECC matches.
+ UE = 0xfe, //< Uncorrectable.
+ E0 = 71, //< Error in ECC bit 0
+ E1 = 70, //< Error in ECC bit 1
+ E2 = 69, //< Error in ECC bit 2
+ E3 = 68, //< Error in ECC bit 3
+ E4 = 67, //< Error in ECC bit 4
+ E5 = 66, //< Error in ECC bit 5
+ E6 = 65, //< Error in ECC bit 6
+ E7 = 64 //< Error in ECC bit 7
+};
+
+extern uint8_t eccmemcpy(uint64_t *dst, uint64_t *src, uint32_t len);
+
+/*
+ * Calculate the size of a buffer if ECC is added
+ *
+ * We add 1 byte of ecc for every 8 bytes of data. So we need to round up to 8
+ * bytes length and then add 1/8
+ */
+#define ECC_SIZE(len) (ALIGN_UP((len), 8) >> 3)
+#define ECC_BUFFER_SIZE(len) (ALIGN_UP((len), 8) + ECC_SIZE(len))
+#define ECC_BUFFER_SIZE_CHECK(len) ((len) % 9)
+#define BUFFER_SIZE_MINUS_ECC(len) ((len) * 8 / 9)
+
+#endif
diff --git a/libflash/Makefile.inc b/libflash/Makefile.inc
index 35f96f7..0850b24 100644
--- a/libflash/Makefile.inc
+++ b/libflash/Makefile.inc
@@ -1,4 +1,4 @@
-LIBFLASH_SRCS = libflash.c libffs.c
+LIBFLASH_SRCS = libflash.c libffs.c ecc.c
LIBFLASH_OBJS = $(LIBFLASH_SRCS:%.c=%.o)
SUBDIRS += libflash
diff --git a/libflash/ecc.c b/libflash/ecc.c
new file mode 100644
index 0000000..9293743
--- /dev/null
+++ b/libflash/ecc.c
@@ -0,0 +1,175 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is based on the hostboot ecc code */
+
+#include <stdint.h>
+#include <ecc.h>
+
+/*
+ * Matrix used for ECC calculation.
+ *
+ * Each row of this is the set of data word bits that are used for
+ * the calculation of the corresponding ECC bit. The parity of the
+ * bitset is the value of the ECC bit.
+ *
+ * ie. ECC[n] = eccMatrix[n] & data
+ *
+ * Note: To make the math easier (and less shifts in resulting code),
+ * row0 = ECC7. HW numbering is MSB, order here is LSB.
+ *
+ * These values come from the HW design of the ECC algorithm.
+ */
+static uint64_t eccmatrix[] = {
+ 0x0000e8423c0f99ff,
+ 0x00e8423c0f99ff00,
+ 0xe8423c0f99ff0000,
+ 0x423c0f99ff0000e8,
+ 0x3c0f99ff0000e842,
+ 0x0f99ff0000e8423c,
+ 0x99ff0000e8423c0f,
+ 0xff0000e8423c0f99
+};
+
+/**
+ * Syndrome calculation matrix.
+ *
+ * Maps syndrome to flipped bit.
+ *
+ * To perform ECC correction, this matrix is a look-up of the bit
+ * that is bad based on the binary difference of the good and bad
+ * ECC. This difference is called the "syndrome".
+ *
+ * When a particular bit is on in the data, it cause a column from
+ * eccMatrix being XOR'd into the ECC field. This column is the
+ * "effect" of each bit. If a bit is flipped in the data then its
+ * "effect" is missing from the ECC. You can calculate ECC on unknown
+ * quality data and compare the ECC field between the calculated
+ * value and the stored value. If the difference is zero, then the
+ * data is clean. If the difference is non-zero, you look up the
+ * difference in the syndrome table to identify the "effect" that
+ * is missing, which is the bit that is flipped.
+ *
+ * Notice that ECC bit flips are recorded by a single "effect"
+ * bit (ie. 0x1, 0x2, 0x4, 0x8 ...) and double bit flips are identified
+ * by the UE status in the table.
+ *
+ * Bits are in MSB order.
+ */
+static uint8_t syndromematrix[] = {
+ GD, E7, E6, UE, E5, UE, UE, 47, E4, UE, UE, 37, UE, 35, 39, UE,
+ E3, UE, UE, 48, UE, 30, 29, UE, UE, 57, 27, UE, 31, UE, UE, UE,
+ E2, UE, UE, 17, UE, 18, 40, UE, UE, 58, 22, UE, 21, UE, UE, UE,
+ UE, 16, 49, UE, 19, UE, UE, UE, 23, UE, UE, UE, UE, 20, UE, UE,
+ E1, UE, UE, 51, UE, 46, 9, UE, UE, 34, 10, UE, 32, UE, UE, 36,
+ UE, 62, 50, UE, 14, UE, UE, UE, 13, UE, UE, UE, UE, UE, UE, UE,
+ UE, 61, 8, UE, 41, UE, UE, UE, 11, UE, UE, UE, UE, UE, UE, UE,
+ 15, UE, UE, UE, UE, UE, UE, UE, UE, UE, 12, UE, UE, UE, UE, UE,
+ E0, UE, UE, 55, UE, 45, 43, UE, UE, 56, 38, UE, 1, UE, UE, UE,
+ UE, 25, 26, UE, 2, UE, UE, UE, 24, UE, UE, UE, UE, UE, 28, UE,
+ UE, 59, 54, UE, 42, UE, UE, 44, 6, UE, UE, UE, UE, UE, UE, UE,
+ 5, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+ UE, 63, 53, UE, 0, UE, UE, UE, 33, UE, UE, UE, UE, UE, UE, UE,
+ 3, UE, UE, 52, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+ 7, UE, UE, UE, UE, UE, UE, UE, UE, 60, UE, UE, UE, UE, UE, UE,
+ UE, UE, UE, UE, 4, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+};
+
+/**
+ * Create the ECC field corresponding to a 8-byte data field
+ *
+ * @data: The 8 byte data to generate ECC for.
+ * @return: The 1 byte ECC corresponding to the data.
+ */
+static uint8_t eccgenerate(uint64_t data)
+{
+ int i;
+ uint8_t result = 0;
+
+ for (i = 0; i < 8; i++)
+ result |= __builtin_parityl(eccmatrix[i] & data) << i;
+
+ return result;
+}
+
+/**
+ * Verify the data and ECC match or indicate how they are wrong.
+ *
+ * @data: The data to check ECC on.
+ * @ecc: The [supposed] ECC for the data.
+ *
+ * @return: eccBitfield or 0-64.
+ *
+ * @retval GD - Indicates the data is good (matches ECC).
+ * @retval UE - Indicates the data is uncorrectable.
+ * @retval all others - Indication of which bit is incorrect.
+ */
+static uint8_t eccverify(uint64_t data, uint8_t ecc)
+{
+ return syndromematrix[eccgenerate(data) ^ ecc];
+}
+
+/**
+ * Copy data from an input buffer with ECC to an output buffer without ECC.
+ * Correct it along the way and check for errors.
+ *
+ * @dst: destination buffer without ECC
+ * @src: source buffer with ECC
+ * @len: number of bytes of data to copy (without ecc).
+ Must be 8 byte aligned.
+ *
+ * @return: eccBitfield or 0-64.
+ *
+ * @retval GD - Data is good.
+ * @retval UE - Data is uncorrectable.
+ * @retval all others - which bit was corrected.
+ */
+uint8_t eccmemcpy(uint64_t *dst, uint64_t *src, uint32_t len)
+{
+ uint64_t *data;
+ uint8_t *ecc;
+ uint32_t i;
+ uint8_t badbit;
+
+ if (len & 0x7) {
+ /* TODO: we could probably handle this */
+ prerror("ECC data length must be 8 byte aligned length:%i\n",
+ len);
+ return UE;
+ }
+
+ /* Handle in chunks of 8 bytes, so adjust the length */
+ len >>= 3;
+
+ for (i = 0; i < len; i++) {
+ data = (uint64_t *)((uint8_t *)src + i*9);
+ ecc = (uint8_t *)data + 8;
+
+ badbit = eccverify(*data, *ecc);
+ if (badbit == UE) {
+ prerror("ECC: uncorrectable error: %016lx %02x\n",
+ (long unsigned int)*data, *ecc);
+ return badbit;
+ }
+ *dst = *data;
+ if (badbit <= UE)
+ prlog(PR_INFO, "ECC: correctable error: %i\n", badbit);
+ if (badbit < 64)
+ *dst = *data ^ (1ul << (63 - badbit));
+ dst++;
+ }
+ return GD;
+}