457 lines
13 KiB
C
457 lines
13 KiB
C
|
/* 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 <inttypes.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <ccan/endian/endian.h>
|
||
|
|
||
|
#include "libflash.h"
|
||
|
#include "ecc.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
|
||
|
/* 0-63 Correctable bit in byte */
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* 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[] = {
|
||
|
0x0000e8423c0f99ffull,
|
||
|
0x00e8423c0f99ff00ull,
|
||
|
0xe8423c0f99ff0000ull,
|
||
|
0x423c0f99ff0000e8ull,
|
||
|
0x3c0f99ff0000e842ull,
|
||
|
0x0f99ff0000e8423cull,
|
||
|
0x99ff0000e8423c0full,
|
||
|
0xff0000e8423c0f99ull
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* 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 enum eccbitfields 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_parityll(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 enum eccbitfields eccverify(uint64_t data, uint8_t ecc)
|
||
|
{
|
||
|
return syndromematrix[eccgenerate(data) ^ ecc];
|
||
|
}
|
||
|
|
||
|
/* IBM bit ordering */
|
||
|
static inline uint64_t eccflipbit(uint64_t data, uint8_t bit)
|
||
|
{
|
||
|
if (bit > 63)
|
||
|
return data;
|
||
|
|
||
|
return data ^ (1ul << (63 - bit));
|
||
|
}
|
||
|
|
||
|
static int eccbyte(uint64_t *dst, struct ecc64 *src)
|
||
|
{
|
||
|
uint8_t ecc, badbit;
|
||
|
uint64_t data;
|
||
|
|
||
|
data = src->data;
|
||
|
ecc = src->ecc;
|
||
|
|
||
|
badbit = eccverify(be64_to_cpu(data), ecc);
|
||
|
if (badbit == UE) {
|
||
|
FL_ERR("ECC: uncorrectable error: %016lx %02x\n",
|
||
|
(long unsigned int)be64_to_cpu(data), ecc);
|
||
|
return badbit;
|
||
|
}
|
||
|
*dst = data;
|
||
|
if (badbit <= UE)
|
||
|
FL_INF("ECC: correctable error: %i\n", badbit);
|
||
|
if (badbit < 64)
|
||
|
*dst = (uint64_t)be64_to_cpu(eccflipbit(be64_to_cpu(data),
|
||
|
badbit));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static uint64_t *inc_uint64_by(const void *p, uint64_t i)
|
||
|
{
|
||
|
return (uint64_t *)(((char *)p) + i);
|
||
|
}
|
||
|
|
||
|
static struct ecc64 *inc_ecc64_by(struct ecc64 *p, uint64_t i)
|
||
|
{
|
||
|
return (struct ecc64 *)(((char *)p) + i);
|
||
|
}
|
||
|
|
||
|
static uint64_t whole_ecc_bytes(uint64_t i)
|
||
|
{
|
||
|
return i & ~(BYTES_PER_ECC - 1);
|
||
|
}
|
||
|
|
||
|
static uint64_t whole_ecc_structs(uint64_t i)
|
||
|
{
|
||
|
return whole_ecc_bytes(i) >> 3;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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: Success or error
|
||
|
*
|
||
|
* @retval: 0 - success
|
||
|
* @retfal: other - fail
|
||
|
*/
|
||
|
int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint64_t len)
|
||
|
{
|
||
|
uint32_t i;
|
||
|
|
||
|
if (len & 0x7) {
|
||
|
/* TODO: we could probably handle this */
|
||
|
FL_ERR("ECC data length must be 8 byte aligned length:%" PRIx64 "\n",
|
||
|
len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Handle in chunks of 8 bytes, so adjust the length */
|
||
|
len >>= 3;
|
||
|
|
||
|
for (i = 0; i < len; i++) {
|
||
|
int rc;
|
||
|
rc = eccbyte(dst, src + i);
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
dst++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy data from an input buffer with ECC to an output buffer without ECC.
|
||
|
* Correct it along the way and check for errors.
|
||
|
*
|
||
|
* Unlike memcmp_from_ecc() which requires that the first byte into
|
||
|
* dst be the first byte in src (which must also be aligned to a
|
||
|
* struct ecc64 struct boundary) this function can cope with the first
|
||
|
* byte in dst not being the first byte in src.
|
||
|
*
|
||
|
* Note: src MUST still be aligned to a struct ecc64 otherwise ECC
|
||
|
* calculations are impossible.
|
||
|
*
|
||
|
* The alignment parameter species the number of bytes present at the
|
||
|
* start of src that should be skipped and not written to dst. Once
|
||
|
* again, these bytes must be in src otherwise the ECC cannot be
|
||
|
* checked.
|
||
|
*
|
||
|
* len also doesn't have any value limitation for this function. Of
|
||
|
* course src must contain an exact multiple of struct ecc64 otherwise
|
||
|
* ECC calculation cannot be performed but this function won't copy
|
||
|
* the entirety of the last src data word if len is not mutiple of 8
|
||
|
*
|
||
|
* @dst: destination buffer without ECC
|
||
|
* @src: source buffer with ECC
|
||
|
* @len: number of bytes of data to copy (without ecc).
|
||
|
* @alignment: number of leading bytes in src which shouldn't be
|
||
|
* copied to dst
|
||
|
* @return: Success or error
|
||
|
*
|
||
|
* @retval: 0 - success
|
||
|
* @retfal: other - fail
|
||
|
*/
|
||
|
int memcpy_from_ecc_unaligned(uint64_t *dst, struct ecc64 *src,
|
||
|
uint64_t len, uint8_t alignment)
|
||
|
{
|
||
|
char data[BYTES_PER_ECC];
|
||
|
uint8_t bytes_wanted;
|
||
|
int rc;
|
||
|
|
||
|
if (alignment > 8)
|
||
|
return -1;
|
||
|
|
||
|
bytes_wanted = BYTES_PER_ECC - alignment;
|
||
|
|
||
|
/*
|
||
|
* Only actually do the first calculation if an alignment is
|
||
|
* required - otherwise jump straight to memcpy_from_ecc()
|
||
|
*/
|
||
|
if (alignment) {
|
||
|
rc = eccbyte((uint64_t *)data, src);
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
|
||
|
memcpy(dst, &data[alignment], bytes_wanted);
|
||
|
|
||
|
src = inc_ecc64_by(src, sizeof(struct ecc64));
|
||
|
dst = inc_uint64_by(dst, bytes_wanted);
|
||
|
len -= bytes_wanted;
|
||
|
}
|
||
|
|
||
|
if (len >= BYTES_PER_ECC) {
|
||
|
rc = memcpy_from_ecc(dst, src, whole_ecc_bytes(len));
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
|
||
|
/*
|
||
|
* It helps to let the compiler to the pointer arithmetic
|
||
|
* here, (dst and src are different types)
|
||
|
*/
|
||
|
dst += whole_ecc_structs(len);
|
||
|
src += whole_ecc_structs(len);
|
||
|
len -= whole_ecc_bytes(len);
|
||
|
}
|
||
|
|
||
|
if (len) {
|
||
|
rc = eccbyte((uint64_t *)data, src);
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
|
||
|
memcpy(dst, data, len);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy data from an input buffer without ECC to an output buffer with ECC.
|
||
|
*
|
||
|
* @dst: destination buffer with ECC
|
||
|
* @src: source buffer without ECC
|
||
|
* @len: number of bytes of data to copy (without ecc, length of src).
|
||
|
* Note: dst must be big enough to hold ecc bytes as well.
|
||
|
* Must be 8 byte aligned.
|
||
|
*
|
||
|
* @return: success or failure
|
||
|
*
|
||
|
* @retval: 0 - success
|
||
|
* @retfal: other - fail
|
||
|
*/
|
||
|
int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint64_t len)
|
||
|
{
|
||
|
struct ecc64 ecc_word;
|
||
|
uint64_t i;
|
||
|
|
||
|
if (len & 0x7) {
|
||
|
/* TODO: we could probably handle this */
|
||
|
FL_ERR("Data to add ECC bytes to must be 8 byte aligned length: %"
|
||
|
PRIx64 "\n", len);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Handle in chunks of 8 bytes, so adjust the length */
|
||
|
len >>= 3;
|
||
|
|
||
|
for (i = 0; i < len; i++) {
|
||
|
ecc_word.ecc = eccgenerate(be64_to_cpu(*(src + i)));
|
||
|
ecc_word.data = *(src + i);
|
||
|
|
||
|
*(dst + i) = ecc_word;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy data from an input buffer without ECC to an output buffer with ECC.
|
||
|
*
|
||
|
* Unlike memcmp_to_ecc() which requires that the first byte in src be
|
||
|
* the first byte of a struct ecc64 structure this function does not
|
||
|
* have this requirement.
|
||
|
*
|
||
|
* Like memcpy_to_ecc_unaligned() the alignment parameter specfies the
|
||
|
* number of bytes in the first src word that are missing and would be
|
||
|
* required to form a struct ecc64 structure.
|
||
|
*
|
||
|
* It must be noted here that extra work IN THE CALLER must be done
|
||
|
* if your data is unaligned. In order to peform ECC calculations
|
||
|
* whatever portions of the ecc words are missing in src must be in
|
||
|
* dst.
|
||
|
*
|
||
|
* For example, if there is an alignment value of 1 then this means
|
||
|
* there is 1 byte (of the total of 8 bytes) missing in src which is
|
||
|
* needed to calculate the first ECC byte. Therefore the first byte of
|
||
|
* dst MUST CONTAIN IT!
|
||
|
*
|
||
|
* The same problem exists for the end of the buffer where src may not
|
||
|
* end exactly aligned, if this is the case dst must contain the
|
||
|
* required bytes to calculate the last ECC byte - they should be in
|
||
|
* dst where they would normally be found if src did contain those
|
||
|
* bytes.
|
||
|
*
|
||
|
* @dst: destination buffer with ECC
|
||
|
* @src: source buffer without ECC
|
||
|
* @len: number of bytes of data to copy (without ecc, length of src).
|
||
|
* @alignment: The number of bytes 'missing' from the start of src to
|
||
|
* be struct ecc64 aligned
|
||
|
*
|
||
|
* Note: dst must be big enough to hold ecc bytes as well.
|
||
|
* Must be 8 byte aligned.
|
||
|
*
|
||
|
* @return: success or failure
|
||
|
*
|
||
|
* @retval: 0 - success
|
||
|
* @retfal: other - fail
|
||
|
*/
|
||
|
|
||
|
int memcpy_to_ecc_unaligned(struct ecc64 *dst, const uint64_t *src,
|
||
|
uint64_t len, uint8_t alignment)
|
||
|
{
|
||
|
struct ecc64 ecc_word;
|
||
|
uint8_t bytes_wanted;
|
||
|
int rc;
|
||
|
|
||
|
bytes_wanted = BYTES_PER_ECC - alignment;
|
||
|
|
||
|
/*
|
||
|
* Only actually do the first calculation if an alignment is
|
||
|
* required - otherwise jump straight to memcpy_to_ecc()
|
||
|
*/
|
||
|
if (alignment) {
|
||
|
ecc_word.data = dst->data;
|
||
|
memcpy(inc_uint64_by(&ecc_word.data, alignment), src, bytes_wanted);
|
||
|
|
||
|
ecc_word.ecc = eccgenerate(be64_to_cpu(ecc_word.data));
|
||
|
memcpy(dst, inc_ecc64_by(&ecc_word, alignment),
|
||
|
sizeof(struct ecc64) - alignment);
|
||
|
|
||
|
dst = inc_ecc64_by(dst, sizeof(struct ecc64) - alignment);
|
||
|
src = inc_uint64_by(src, bytes_wanted);
|
||
|
len -= bytes_wanted;
|
||
|
}
|
||
|
|
||
|
if (len >= BYTES_PER_ECC) {
|
||
|
rc = memcpy_to_ecc(dst, src, whole_ecc_bytes(len));
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
|
||
|
/*
|
||
|
* It helps to let the compiler to the pointer arithmetic
|
||
|
* here, (dst and src are different types)
|
||
|
*/
|
||
|
dst += whole_ecc_structs(len);
|
||
|
src += whole_ecc_structs(len);
|
||
|
len -= whole_ecc_bytes(len);
|
||
|
}
|
||
|
|
||
|
if (len) {
|
||
|
bytes_wanted = BYTES_PER_ECC - len;
|
||
|
|
||
|
ecc_word.data = *src;
|
||
|
memcpy(inc_uint64_by(&ecc_word.data, len), inc_ecc64_by(dst, len),
|
||
|
bytes_wanted);
|
||
|
ecc_word.ecc = eccgenerate(be64_to_cpu(ecc_word.data));
|
||
|
|
||
|
*dst = ecc_word;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|