diff options
Diffstat (limited to 'clib/src/ecc.c')
-rw-r--r-- | clib/src/ecc.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/clib/src/ecc.c b/clib/src/ecc.c new file mode 100644 index 0000000..2103b00 --- /dev/null +++ b/clib/src/ecc.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: ecc.c + * Author: Shaun Wetzstein <shaun@us.ibm.com> + * Descr: SFC ECC functions + * Note: + * Date: 08/02/12 + * Descr: Added New ECC function with correctable bit functionality. + * Date: 12/04/13 + */ + +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdbool.h> +#include <malloc.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <ctype.h> +#include <endian.h> +#include <assert.h> + +#include "ecc.h" +#include "builtin.h" +#include "attribute.h" + +#ifndef be64toh +#include <byteswap.h> +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define be64toh(x) __bswap_64(x) +#define htobe64(x) __bswap_64(x) +#else +#define be64toh(x) (x) +#define htobe64(x) (x) +#endif +#endif + +/* + * This is an alternative way to calculate the ECC byte taken + * from the SFC spec. + */ +uint8_t sfc_ecc2(uint8_t __data[8]) __unused__; +uint8_t sfc_ecc2(uint8_t __data[8]) +{ + uint8_t ecc = 0; + + for (int byte = 0; byte < 8; byte++) { + for (int bit = 0; bit < 8; bit++) { + static unsigned char m[] = + { 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99 }; + + unsigned char x = + __data[byte] & m[(byte + bit + 1) & 7]; + x = x ^ (x >> 4); + x = x ^ (x >> 2); + x = x ^ (x >> 1); + + ecc ^= (x & 1) << bit; + } + } + + return ~ecc; +} + +uint8_t sfc_ecc(uint8_t __data[8]) +{ +/* each bit of the ECC data corresponds to a row in this matrix */ + static uint8_t __matrix[8][8] = { +/* 11111111 00000000 00000000 11101000 01000010 00111100 00001111 10011001 */ + {0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99}, +/* 10011001 11111111 00000000 00000000 11101000 01000010 00111100 00001111 */ + {0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f}, +/* 00001111 10011001 11111111 00000000 00000000 11101000 01000010 00111100 */ + {0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c}, +/* 00111100 00001111 10011001 11111111 00000000 00000000 11101000 01000010 */ + {0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42}, +/* 01000010 00111100 00001111 10011001 11111111 00000000 00000000 11101000 */ + {0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8}, +/* 11101000 01000010 00111100 00001111 10011001 11111111 00000000 00000000 */ + {0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00}, +/* 00000000 11101000 01000010 00111100 00001111 10011001 11111111 00000000 */ + {0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00}, +/* 00000000 00000000 11101000 01000010 00111100 00001111 10011001 11111111 */ + {0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff}, + }; + + static uint8_t __mask[] = { +/* 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 */ + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 + }; + + uint8_t __and[8], __ecc = 0; + + for (uint32_t i = 0; i < sizeof(__matrix) / sizeof(*__matrix); i++) { + int __popcount = 0; + + for (uint32_t __byte = 0; __byte < 8; __byte++) { + /* compute the AND of the data and ECC matrix */ + __and[__byte] = __data[__byte] & __matrix[i][__byte]; + /* count the number of '1' bits in the result (parity) */ + __popcount += popcount(__and[__byte]); + } + + /* if the result is odd parity, turn on corresponding ECC bit */ + if ((__popcount) & 1) /* odd parity? */ + __ecc |= __mask[i]; + +#ifdef DEBUG + printf("\nmatrix["); + print_binary(NULL, __matrix[i], 8); + printf("]\n data["); + print_binary(NULL, __data, 8); + printf("]\n and["); + print_binary(NULL, __and, 8); + printf("]\n"); + printf("popcount: %d ecc %2.2x\n", __popcount, __ecc); +#endif + } + + /* the ECC data is inverted such that */ + /* 0xFFFFFFFFffffffff => 0xFF for erased NOR flash */ + return __ecc ^ 0xFF; +} + + +/* + * "aaaaaaaa [wwwwwwww_xxxxxxxx e yyyyyyyy_zzzzzzzz e] [........ ........]" + */ +static void __ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz, bool invert) +{ + if (__buf_sz <= 0 || __buf == NULL) + return; + if (__out == NULL) + __out = stdout; + + size_t hex_size = (16 + 2) * 2; // 16 bytes per doubleword plus ECC byte + size_t ascii_size = (8 + 1) * 2; // 8 bytes per doublewod plus ECC byte + + uint8_t hex[hex_size + 1], ascii[hex_size + 1]; + memset(hex, '.', hex_size), memset(ascii, '.', ascii_size); + + void print_line(void) { + const char *ansi_red = "\033[1;1m\033[1;31m"; + const char *ansi_norm = "\033[0m"; + + uint8_t c1 = sfc_ecc(ascii + 0), e1 = ascii[8]; + if (invert == true) + c1 = ~c1; + uint8_t c2 = sfc_ecc(ascii + 9), e2 = ascii[17]; + if (invert == true) + c2 = ~c2; + + for (size_t i = 0; i < ascii_size; i++) + ascii[i] = isprint(ascii[i]) ? ascii[i] : '.'; + + fprintf(__out, "%08x ", __addr); + + fprintf(__out, + "[%.8s_%.8s %.*s%.2s%.*s %.8s_%.8s %.*s%.2s%.*s] ", + hex + 0, hex + 8, (c1 != e1) ? strlen(ansi_red) : 0, + ansi_red, hex + 16, (c1 != e1) ? strlen(ansi_norm) : 0, + ansi_norm, hex + 18, hex + 26, + (c2 != e2) ? strlen(ansi_red) : 0, ansi_red, hex + 34, + (c2 != e2) ? strlen(ansi_norm) : 0, ansi_norm); + + fprintf(__out, "[%.8s %.8s]\n", ascii + 0, ascii + 9); + } + + size_t s = __addr % 16, i = 0; + __addr &= ~0xF; + + for (i = s; i < __buf_sz + s; i++) { + unsigned char c = ((unsigned char *)__buf)[i - s]; + + hex[((i << 1) + 0) % hex_size] = "0123456789abcdef"[c >> 4]; + hex[((i << 1) + 1) % hex_size] = "0123456789abcdef"[c & 0xf]; + + ascii[i % ascii_size] = c; + + if (i == 0) + continue; + + if ((i + 1) % ascii_size == 0) { + print_line(); + memset(hex, '.', hex_size), memset(ascii, '.', + ascii_size); + + __addr += ascii_size; + } + } + + if (i % ascii_size) + print_line(); + + return; +} + + + +/* ======================================== */ +static uint64_t ecc_matrix[] = { + //0000000000000000111010000100001000111100000011111001100111111111 + 0x0000e8423c0f99ff, + //0000000011101000010000100011110000001111100110011111111100000000 + 0x00e8423c0f99ff00, + //1110100001000010001111000000111110011001111111110000000000000000 + 0xe8423c0f99ff0000, + //0100001000111100000011111001100111111111000000000000000011101000 + 0x423c0f99ff0000e8, + //0011110000001111100110011111111100000000000000001110100001000010 + 0x3c0f99ff0000e842, + //0000111110011001111111110000000000000000111010000100001000111100 + 0x0f99ff0000e8423c, + //1001100111111111000000000000000011101000010000100011110000001111 + 0x99ff0000e8423c0f, + //1111111100000000000000001110100001000010001111000000111110011001 + 0xff0000e8423c0f99 +}; + +static uint8_t syndrome_matrix[] = { + 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, +}; + + +static uint8_t generate_ecc(uint64_t i_data) +{ + uint8_t result = 0; + + for (int i = 0; i < 8; i++) + { + result |= __builtin_parityll(ecc_matrix[i] & i_data) << i; + } + return result; +} +static uint8_t verify_ecc(uint64_t i_data, uint8_t i_ecc) +{ + return syndrome_matrix[generate_ecc(i_data) ^ i_ecc ]; +} +static uint8_t correct_ecc(uint64_t *io_data, uint8_t *io_ecc) +{ + uint8_t bad_bit = verify_ecc(*io_data, *io_ecc); + + if ((bad_bit != GD) && (bad_bit != UE)) // Good is done, UE is hopeless. + { + // Determine if the ECC or data part is bad, do bit flip. + if (bad_bit >= E7) + { + *io_ecc ^= (1 << (bad_bit - E7)); + } + else + { + *io_data ^=(1ull << (63 - bad_bit)); + } + } + return bad_bit; +} + +static void inject_ecc(const uint8_t* i_src, size_t i_srcSz, + uint8_t* o_dst, bool invert) +{ + assert(0 == (i_srcSz % sizeof(uint64_t))); + + for(size_t i = 0, o = 0; + i < i_srcSz; + i += sizeof(uint64_t), o += sizeof(uint64_t) + sizeof(uint8_t)) + { + // Read data word, copy to destination. + uint64_t data = *(const uint64_t*)(&i_src[i]); + + *(uint64_t*)(&o_dst[o]) = data; + data = be64toh(data); + + // Calculate ECC, copy to destination. + uint8_t ecc = generate_ecc(data); + o_dst[o + sizeof(uint64_t)] = invert ? ~ecc : ecc; + } +} +static ecc_status_t remove_ecc(uint8_t* io_src, size_t i_srcSz, + uint8_t* o_dst, size_t i_dstSz, + bool invert) +{ + assert(0 == (i_dstSz % sizeof(uint64_t))); + + ecc_status_t rc = CLEAN; + + for(size_t i = 0, o = 0; + i < i_srcSz; + i += sizeof(uint64_t) + sizeof(uint8_t), o += sizeof(uint64_t)) + { + // Read data and ECC parts. + uint64_t data = *(uint64_t*)(&io_src[i]); + data = be64toh(data); + + uint8_t ecc = io_src[i + sizeof(uint64_t)]; + if(invert) + { + ecc = ~ecc; + } + // Calculate failing bit and fix data. + uint8_t bad_bit = correct_ecc(&data, &ecc); + + // Return data to big endian. + data = htobe64(data); + + // Perform correction and status update. + if (bad_bit == UE) + { + rc = UNCORRECTABLE; + } + else if (bad_bit != GD) + { + if (rc != UNCORRECTABLE) + { + rc = CORRECTED; + } + *(uint64_t*)(&io_src[i]) = data; + io_src[i + sizeof(uint64_t)] = invert ? ~ecc : ecc; + } + + // Copy fixed data to destination buffer. + *(uint64_t*)(&o_dst[o]) = data; + } + return rc; +} +/* ========================================= */ + +static ssize_t __ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz, + bool invert) +{ + int __size = sizeof(uint64_t); + + errno = 0; + if (__src_sz & (__size - 1)) { + errno = EINVAL; + return -1; + } + if (__dst_sz < (__src_sz + (__src_sz / __size))) { + errno = ENOBUFS; + return -1; + } + + ssize_t rc=0; + ssize_t c_sz = __src_sz; + for (; c_sz; c_sz -= __size) { + rc += __size + 1; + } + + inject_ecc(__src, __src_sz, __dst, invert); + return (rc); +} + +static ssize_t __ecc_remove(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz, + bool invert) +{ + int __size = sizeof(uint64_t); + + errno = 0; + if ((__src_sz % (__size + 1)) != 0) { + errno = EINVAL; + return -1; + } + if (__dst_sz < (__src_sz - (__src_sz / __size))) { + errno = ENOBUFS; + return -1; + } + + + int target_size = ((__src_sz / (sizeof(uint64_t) + 1))*sizeof(uint64_t)); + if( remove_ecc((uint8_t*)__src, __src_sz, __dst, __dst_sz, invert) != CLEAN) + { + target_size = 0; + } + return target_size; +} + +void sfc_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +{ + return __ecc_dump(__out, __addr, __buf, __buf_sz, false); +} + +/* ========================================= */ +ssize_t sfc_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_inject(__dst, __dst_sz, __src, __src_sz, true); +} +ssize_t sfc_ecc_remove(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_remove(__dst, __dst_sz, __src, __src_sz, true); + +} +ssize_t p8_ecc_remove_size (void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, size_t __src_sz __unused__) +{ + return __ecc_remove(__dst, __dst_sz, __src, __src_sz, false); +} + +/* ========================================= */ +ssize_t p8_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_inject(__dst, __dst_sz, __src, __src_sz, false); +} + +ecc_status_t p8_ecc_remove (void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, size_t __src_sz __unused__) +{ + return remove_ecc(__src, __src_sz, __dst, __dst_sz, false); +} + +void p8_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +{ + return __ecc_dump(__out, __addr, __buf, __buf_sz, true); +} + |