LCOV - code coverage report
Current view: top level - external/ffspart/libflash - ecc.c (source / functions) Hit Total Coverage
Test: skiboot.info Lines: 15 105 14.3 %
Date: 2024-09-10 18:37:41 Functions: 1 6 16.7 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  * This is based on the hostboot ecc code
       4                 :            :  *
       5                 :            :  * Copyright 2013-2019 IBM Corp.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <stdint.h>
       9                 :            : #include <inttypes.h>
      10                 :            : #include <string.h>
      11                 :            : 
      12                 :            : #include <ccan/endian/endian.h>
      13                 :            : 
      14                 :            : #include "libflash.h"
      15                 :            : #include "ecc.h"
      16                 :            : 
      17                 :            : /* Bit field identifiers for syndrome calculations. */
      18                 :            : enum eccbitfields
      19                 :            : {
      20                 :            :         GD = 0xff,      //< Good, ECC matches.
      21                 :            :         UE = 0xfe,      //< Uncorrectable.
      22                 :            :         E0 = 71,        //< Error in ECC bit 0
      23                 :            :         E1 = 70,        //< Error in ECC bit 1
      24                 :            :         E2 = 69,        //< Error in ECC bit 2
      25                 :            :         E3 = 68,        //< Error in ECC bit 3
      26                 :            :         E4 = 67,        //< Error in ECC bit 4
      27                 :            :         E5 = 66,        //< Error in ECC bit 5
      28                 :            :         E6 = 65,        //< Error in ECC bit 6
      29                 :            :         E7 = 64         //< Error in ECC bit 7
      30                 :            :         /* 0-63 Correctable bit in byte */
      31                 :            : };
      32                 :            : 
      33                 :            : /*
      34                 :            :  * Matrix used for ECC calculation.
      35                 :            :  *
      36                 :            :  *  Each row of this is the set of data word bits that are used for
      37                 :            :  *  the calculation of the corresponding ECC bit.  The parity of the
      38                 :            :  *  bitset is the value of the ECC bit.
      39                 :            :  *
      40                 :            :  *  ie. ECC[n] = eccMatrix[n] & data
      41                 :            :  *
      42                 :            :  *  Note: To make the math easier (and less shifts in resulting code),
      43                 :            :  *        row0 = ECC7.  HW numbering is MSB, order here is LSB.
      44                 :            :  *
      45                 :            :  *  These values come from the HW design of the ECC algorithm.
      46                 :            :  */
      47                 :            : static uint64_t eccmatrix[] = {
      48                 :            :         0x0000e8423c0f99ffull,
      49                 :            :         0x00e8423c0f99ff00ull,
      50                 :            :         0xe8423c0f99ff0000ull,
      51                 :            :         0x423c0f99ff0000e8ull,
      52                 :            :         0x3c0f99ff0000e842ull,
      53                 :            :         0x0f99ff0000e8423cull,
      54                 :            :         0x99ff0000e8423c0full,
      55                 :            :         0xff0000e8423c0f99ull
      56                 :            : };
      57                 :            : 
      58                 :            : /**
      59                 :            :  * Syndrome calculation matrix.
      60                 :            :  *
      61                 :            :  *  Maps syndrome to flipped bit.
      62                 :            :  *
      63                 :            :  *  To perform ECC correction, this matrix is a look-up of the bit
      64                 :            :  *  that is bad based on the binary difference of the good and bad
      65                 :            :  *  ECC.  This difference is called the "syndrome".
      66                 :            :  *
      67                 :            :  *  When a particular bit is on in the data, it cause a column from
      68                 :            :  *  eccMatrix being XOR'd into the ECC field.  This column is the
      69                 :            :  *  "effect" of each bit.  If a bit is flipped in the data then its
      70                 :            :  *  "effect" is missing from the ECC.  You can calculate ECC on unknown
      71                 :            :  *  quality data and compare the ECC field between the calculated
      72                 :            :  *  value and the stored value.  If the difference is zero, then the
      73                 :            :  *  data is clean.  If the difference is non-zero, you look up the
      74                 :            :  *  difference in the syndrome table to identify the "effect" that
      75                 :            :  *  is missing, which is the bit that is flipped.
      76                 :            :  *
      77                 :            :  *  Notice that ECC bit flips are recorded by a single "effect"
      78                 :            :  *  bit (ie. 0x1, 0x2, 0x4, 0x8 ...) and double bit flips are identified
      79                 :            :  *  by the UE status in the table.
      80                 :            :  *
      81                 :            :  *  Bits are in MSB order.
      82                 :            :  */
      83                 :            : static enum eccbitfields syndromematrix[] = {
      84                 :            :         GD, E7, E6, UE, E5, UE, UE, 47, E4, UE, UE, 37, UE, 35, 39, UE,
      85                 :            :         E3, UE, UE, 48, UE, 30, 29, UE, UE, 57, 27, UE, 31, UE, UE, UE,
      86                 :            :         E2, UE, UE, 17, UE, 18, 40, UE, UE, 58, 22, UE, 21, UE, UE, UE,
      87                 :            :         UE, 16, 49, UE, 19, UE, UE, UE, 23, UE, UE, UE, UE, 20, UE, UE,
      88                 :            :         E1, UE, UE, 51, UE, 46,  9, UE, UE, 34, 10, UE, 32, UE, UE, 36,
      89                 :            :         UE, 62, 50, UE, 14, UE, UE, UE, 13, UE, UE, UE, UE, UE, UE, UE,
      90                 :            :         UE, 61,  8, UE, 41, UE, UE, UE, 11, UE, UE, UE, UE, UE, UE, UE,
      91                 :            :         15, UE, UE, UE, UE, UE, UE, UE, UE, UE, 12, UE, UE, UE, UE, UE,
      92                 :            :         E0, UE, UE, 55, UE, 45, 43, UE, UE, 56, 38, UE,  1, UE, UE, UE,
      93                 :            :         UE, 25, 26, UE,  2, UE, UE, UE, 24, UE, UE, UE, UE, UE, 28, UE,
      94                 :            :         UE, 59, 54, UE, 42, UE, UE, 44,  6, UE, UE, UE, UE, UE, UE, UE,
      95                 :            :          5, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
      96                 :            :         UE, 63, 53, UE,  0, UE, UE, UE, 33, UE, UE, UE, UE, UE, UE, UE,
      97                 :            :          3, UE, UE, 52, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
      98                 :            :          7, UE, UE, UE, UE, UE, UE, UE, UE, 60, UE, UE, UE, UE, UE, UE,
      99                 :            :         UE, UE, UE, UE,  4, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
     100                 :            : };
     101                 :            : 
     102                 :            : /**
     103                 :            :  * Create the ECC field corresponding to a 8-byte data field
     104                 :            :  *
     105                 :            :  *  @data:      The 8 byte data to generate ECC for.
     106                 :            :  *  @return:    The 1 byte ECC corresponding to the data.
     107                 :            :  */
     108                 :       2268 : static uint8_t eccgenerate(uint64_t data)
     109                 :            : {
     110                 :       2268 :         int i;
     111                 :       2268 :         uint8_t result = 0;
     112                 :            : 
     113                 :      20412 :         for (i = 0; i < 8; i++)
     114                 :      18144 :                 result |= __builtin_parityll(eccmatrix[i] & data) << i;
     115                 :            : 
     116                 :       2268 :         return result;
     117                 :            : }
     118                 :            : 
     119                 :            : /**
     120                 :            :  * Verify the data and ECC match or indicate how they are wrong.
     121                 :            :  *
     122                 :            :  * @data:       The data to check ECC on.
     123                 :            :  * @ecc:        The [supposed] ECC for the data.
     124                 :            :  *
     125                 :            :  * @return:     eccBitfield or 0-64.
     126                 :            :  *
     127                 :            :  * @retval GD - Indicates the data is good (matches ECC).
     128                 :            :  * @retval UE - Indicates the data is uncorrectable.
     129                 :            :  * @retval all others - Indication of which bit is incorrect.
     130                 :            :  */
     131                 :          0 : static enum eccbitfields eccverify(uint64_t data, uint8_t ecc)
     132                 :            : {
     133                 :          0 :         return syndromematrix[eccgenerate(data) ^ ecc];
     134                 :            : }
     135                 :            : 
     136                 :            : /* IBM bit ordering */
     137                 :          0 : static inline uint64_t eccflipbit(uint64_t data, uint8_t bit)
     138                 :            : {
     139                 :          0 :         if (bit > 63)
     140                 :            :                 return data;
     141                 :            : 
     142                 :          0 :         return data ^ (1ul << (63 - bit));
     143                 :            : }
     144                 :            : 
     145                 :          0 : static int eccbyte(beint64_t *dst, struct ecc64 *src)
     146                 :            : {
     147                 :          0 :         uint8_t ecc, badbit;
     148                 :          0 :         uint64_t data;
     149                 :            : 
     150                 :          0 :         data = be64_to_cpu(src->data);
     151                 :          0 :         ecc = src->ecc;
     152                 :            : 
     153                 :          0 :         badbit = eccverify(data, ecc);
     154                 :          0 :         if (badbit == UE) {
     155                 :          0 :                 FL_ERR("ECC: uncorrectable error: %016llx %02x\n", (unsigned long long int)data, ecc);
     156                 :          0 :                 return badbit;
     157                 :            :         }
     158                 :          0 :         if (badbit <= UE)
     159                 :          0 :                 FL_INF("ECC: correctable error: %i\n", badbit);
     160                 :          0 :         if (badbit < 64)
     161                 :          0 :                 *dst = cpu_to_be64(eccflipbit(data, badbit));
     162                 :            :         else
     163                 :          0 :                 *dst = cpu_to_be64(data);
     164                 :            : 
     165                 :            :         return 0;
     166                 :            : }
     167                 :            : 
     168                 :          0 : static beint64_t *inc_beint64_by(const void *p, uint64_t i)
     169                 :            : {
     170                 :          0 :         return (beint64_t *)(((char *)p) + i);
     171                 :            : }
     172                 :            : 
     173                 :          0 : static uint64_t *inc_uint64_by(const void *p, uint64_t i)
     174                 :            : {
     175                 :          0 :         return (uint64_t *)(((char *)p) + i);
     176                 :            : }
     177                 :            : 
     178                 :          0 : static struct ecc64 *inc_ecc64_by(struct ecc64 *p, uint64_t i)
     179                 :            : {
     180                 :          0 :         return (struct ecc64 *)(((char *)p) + i);
     181                 :            : }
     182                 :            : 
     183                 :          0 : static uint64_t whole_ecc_bytes(uint64_t i)
     184                 :            : {
     185                 :          0 :         return i & ~(BYTES_PER_ECC - 1);
     186                 :            : }
     187                 :            : 
     188                 :            : static uint64_t whole_ecc_structs(uint64_t i)
     189                 :            : {
     190                 :          0 :         return whole_ecc_bytes(i) >> 3;
     191                 :            : }
     192                 :            : 
     193                 :            : /**
     194                 :            :  * Copy data from an input buffer with ECC to an output buffer without ECC.
     195                 :            :  * Correct it along the way and check for errors.
     196                 :            :  *
     197                 :            :  * @dst:        destination buffer without ECC
     198                 :            :  * @src:        source buffer with ECC
     199                 :            :  * @len:        number of bytes of data to copy (without ecc).
     200                 :            :  *                   Must be 8 byte aligned.
     201                 :            :  *
     202                 :            :  * @return:     Success or error
     203                 :            :  *
     204                 :            :  * @retval: 0 - success
     205                 :            :  * @retfal: other - fail
     206                 :            :  */
     207                 :          0 : int memcpy_from_ecc(beint64_t *dst, struct ecc64 *src, uint64_t len)
     208                 :            : {
     209                 :          0 :         uint32_t i;
     210                 :            : 
     211                 :          0 :         if (len & 0x7) {
     212                 :            :                 /* TODO: we could probably handle this */
     213                 :          0 :                 FL_ERR("ECC data length must be 8 byte aligned length:%" PRIx64  "\n",
     214                 :            :                        len);
     215                 :          0 :                 return -1;
     216                 :            :         }
     217                 :            : 
     218                 :            :         /* Handle in chunks of 8 bytes, so adjust the length */
     219                 :          0 :         len >>= 3;
     220                 :            : 
     221                 :          0 :         for (i = 0; i < len; i++) {
     222                 :          0 :                 int rc;
     223                 :          0 :                 rc = eccbyte(dst, src + i);
     224                 :          0 :                 if (rc)
     225                 :          0 :                         return rc;
     226                 :          0 :                 dst++;
     227                 :            :         }
     228                 :            :         return 0;
     229                 :            : }
     230                 :            : 
     231                 :            : /**
     232                 :            :  * Copy data from an input buffer with ECC to an output buffer without ECC.
     233                 :            :  * Correct it along the way and check for errors.
     234                 :            :  *
     235                 :            :  * Unlike memcmp_from_ecc() which requires that the first byte into
     236                 :            :  * dst be the first byte in src (which must also be aligned to a
     237                 :            :  * struct ecc64 struct boundary) this function can cope with the first
     238                 :            :  * byte in dst not being the first byte in src.
     239                 :            :  *
     240                 :            :  * Note: src MUST still be aligned to a struct ecc64 otherwise ECC
     241                 :            :  * calculations are impossible.
     242                 :            :  *
     243                 :            :  * The alignment parameter species the number of bytes present at the
     244                 :            :  * start of src that should be skipped and not written to dst. Once
     245                 :            :  * again, these bytes must be in src otherwise the ECC cannot be
     246                 :            :  * checked.
     247                 :            :  *
     248                 :            :  * len also doesn't have any value limitation for this function. Of
     249                 :            :  * course src must contain an exact multiple of struct ecc64 otherwise
     250                 :            :  * ECC calculation cannot be performed but this function won't copy
     251                 :            :  * the entirety of the last src data word if len is not mutiple of 8
     252                 :            :  *
     253                 :            :  * @dst:                destination buffer without ECC
     254                 :            :  * @src:                source buffer with ECC
     255                 :            :  * @len:                number of bytes of data to copy (without ecc).
     256                 :            :  * @alignment:  number of leading bytes in src which shouldn't be
     257                 :            :  *                              copied to dst
     258                 :            :  * @return:     Success or error
     259                 :            :  *
     260                 :            :  * @retval: 0 - success
     261                 :            :  * @retfal: other - fail
     262                 :            :  */
     263                 :          0 : int memcpy_from_ecc_unaligned(beint64_t *dst, struct ecc64 *src,
     264                 :            :                 uint64_t len, uint8_t alignment)
     265                 :            : {
     266                 :          0 :         char data[BYTES_PER_ECC];
     267                 :          0 :         uint8_t bytes_wanted;
     268                 :          0 :         int rc;
     269                 :            : 
     270                 :          0 :         if (alignment > 8)
     271                 :            :                 return -1;
     272                 :            : 
     273                 :          0 :         bytes_wanted = BYTES_PER_ECC - alignment;
     274                 :            : 
     275                 :            :         /*
     276                 :            :          * Only actually do the first calculation if an alignment is
     277                 :            :          * required - otherwise jump straight to memcpy_from_ecc()
     278                 :            :          */
     279                 :          0 :         if (alignment) {
     280                 :          0 :                 rc = eccbyte((beint64_t *)data, src);
     281                 :          0 :                 if (rc)
     282                 :            :                         return rc;
     283                 :            : 
     284                 :          0 :                 memcpy(dst, &data[alignment], bytes_wanted);
     285                 :            : 
     286                 :          0 :                 src = inc_ecc64_by(src, sizeof(struct ecc64));
     287                 :          0 :                 dst = inc_beint64_by(dst, bytes_wanted);
     288                 :          0 :                 len -= bytes_wanted;
     289                 :            :         }
     290                 :            : 
     291                 :          0 :         if (len >= BYTES_PER_ECC) {
     292                 :          0 :                 rc = memcpy_from_ecc(dst, src, whole_ecc_bytes(len));
     293                 :          0 :                 if (rc)
     294                 :            :                         return rc;
     295                 :            : 
     296                 :            :                 /*
     297                 :            :                  * It helps to let the compiler to the pointer arithmetic
     298                 :            :                  * here, (dst and src are different types)
     299                 :            :                  */
     300                 :          0 :                 dst += whole_ecc_structs(len);
     301                 :          0 :                 src += whole_ecc_structs(len);
     302                 :          0 :                 len -= whole_ecc_bytes(len);
     303                 :            :         }
     304                 :            : 
     305                 :          0 :         if (len) {
     306                 :          0 :                 rc = eccbyte((beint64_t *)data, src);
     307                 :          0 :                 if (rc)
     308                 :            :                         return rc;
     309                 :            : 
     310                 :          0 :                 memcpy(dst, data, len);
     311                 :            :         }
     312                 :            : 
     313                 :            :         return 0;
     314                 :            : }
     315                 :            : 
     316                 :            : /**
     317                 :            :  * Copy data from an input buffer without ECC to an output buffer with ECC.
     318                 :            :  *
     319                 :            :  * @dst:        destination buffer with ECC
     320                 :            :  * @src:        source buffer without ECC
     321                 :            :  * @len:        number of bytes of data to copy (without ecc, length of src).
     322                 :            :  *       Note: dst must be big enough to hold ecc bytes as well.
     323                 :            :  *                   Must be 8 byte aligned.
     324                 :            :  *
     325                 :            :  * @return:     success or failure
     326                 :            :  *
     327                 :            :  * @retval: 0 - success
     328                 :            :  * @retfal: other - fail
     329                 :            :  */
     330                 :         20 : int memcpy_to_ecc(struct ecc64 *dst, const beint64_t *src, uint64_t len)
     331                 :            : {
     332                 :         20 :         struct ecc64 ecc_word;
     333                 :         20 :         uint64_t i;
     334                 :            : 
     335                 :         20 :         if (len & 0x7) {
     336                 :            :                 /* TODO: we could probably handle this */
     337                 :          0 :                 FL_ERR("Data to add ECC bytes to must be 8 byte aligned length: %"
     338                 :            :                        PRIx64 "\n", len);
     339                 :          0 :                 return -1;
     340                 :            :         }
     341                 :            : 
     342                 :            :         /* Handle in chunks of 8 bytes, so adjust the length */
     343                 :         20 :         len >>= 3;
     344                 :            : 
     345                 :       2288 :         for (i = 0; i < len; i++) {
     346                 :       2268 :                 ecc_word.ecc = eccgenerate(be64_to_cpu(*(src + i)));
     347                 :       2268 :                 ecc_word.data = *(src + i);
     348                 :            : 
     349                 :       2268 :                 *(dst + i) = ecc_word;
     350                 :            :         }
     351                 :            : 
     352                 :            :         return 0;
     353                 :            : }
     354                 :            : 
     355                 :            : /**
     356                 :            :  * Copy data from an input buffer without ECC to an output buffer with ECC.
     357                 :            :  *
     358                 :            :  * Unlike memcmp_to_ecc() which requires that the first byte in src be
     359                 :            :  * the first byte of a struct ecc64 structure this function does not
     360                 :            :  * have this requirement.
     361                 :            :  *
     362                 :            :  * Like memcpy_to_ecc_unaligned() the alignment parameter specfies the
     363                 :            :  * number of bytes in the first src word that are missing and would be
     364                 :            :  * required to form a struct ecc64 structure.
     365                 :            :  *
     366                 :            :  * It must be noted here that extra work IN THE CALLER must be done
     367                 :            :  * if your data is unaligned. In order to peform ECC calculations
     368                 :            :  * whatever portions of the ecc words are missing in src must be in
     369                 :            :  * dst.
     370                 :            :  *
     371                 :            :  * For example, if there is an alignment value of 1 then this means
     372                 :            :  * there is 1 byte (of the total of 8 bytes) missing in src which is
     373                 :            :  * needed to calculate the first ECC byte. Therefore the first byte of
     374                 :            :  * dst MUST CONTAIN IT!
     375                 :            :  *
     376                 :            :  * The same problem exists for the end of the buffer where src may not
     377                 :            :  * end exactly aligned, if this is the case dst must contain the
     378                 :            :  * required bytes to calculate the last ECC byte - they should be in
     379                 :            :  * dst where they would normally be found if src did contain those
     380                 :            :  * bytes.
     381                 :            :  *
     382                 :            :  * @dst:                destination buffer with ECC
     383                 :            :  * @src:                source buffer without ECC
     384                 :            :  * @len:                number of bytes of data to copy (without ecc, length of src).
     385                 :            :  * @alignment:  The number of bytes 'missing' from the start of src to
     386                 :            :  *                              be struct ecc64 aligned
     387                 :            :  *
     388                 :            :  *       Note: dst must be big enough to hold ecc bytes as well.
     389                 :            :  *                   Must be 8 byte aligned.
     390                 :            :  *
     391                 :            :  * @return:     success or failure
     392                 :            :  *
     393                 :            :  * @retval: 0 - success
     394                 :            :  * @retfal: other - fail
     395                 :            :  */
     396                 :            : 
     397                 :          0 : int memcpy_to_ecc_unaligned(struct ecc64 *dst, const beint64_t *src,
     398                 :            :                 uint64_t len, uint8_t alignment)
     399                 :            : {
     400                 :          0 :         struct ecc64 ecc_word;
     401                 :          0 :         uint8_t bytes_wanted;
     402                 :          0 :         int rc;
     403                 :            : 
     404                 :          0 :         bytes_wanted = BYTES_PER_ECC - alignment;
     405                 :            : 
     406                 :            :         /*
     407                 :            :          * Only actually do the first calculation if an alignment is
     408                 :            :          * required - otherwise jump straight to memcpy_to_ecc()
     409                 :            :          */
     410                 :          0 :         if (alignment) {
     411                 :          0 :                 ecc_word.data = dst->data;
     412                 :          0 :                 memcpy(inc_uint64_by(&ecc_word.data, alignment), src, bytes_wanted);
     413                 :            : 
     414                 :          0 :                 ecc_word.ecc = eccgenerate(be64_to_cpu(ecc_word.data));
     415                 :          0 :                 memcpy(dst, inc_ecc64_by(&ecc_word, alignment),
     416                 :            :                                 sizeof(struct ecc64) - alignment);
     417                 :            : 
     418                 :          0 :                 dst = inc_ecc64_by(dst, sizeof(struct ecc64) - alignment);
     419                 :          0 :                 src = inc_beint64_by(src, bytes_wanted);
     420                 :          0 :                 len -= bytes_wanted;
     421                 :            :         }
     422                 :            : 
     423                 :          0 :         if (len >= BYTES_PER_ECC) {
     424                 :          0 :                 rc = memcpy_to_ecc(dst, src, whole_ecc_bytes(len));
     425                 :          0 :                 if (rc)
     426                 :            :                         return rc;
     427                 :            : 
     428                 :            :                 /*
     429                 :            :                  * It helps to let the compiler to the pointer arithmetic
     430                 :            :                  * here, (dst and src are different types)
     431                 :            :                  */
     432                 :          0 :                 dst += whole_ecc_structs(len);
     433                 :          0 :                 src += whole_ecc_structs(len);
     434                 :          0 :                 len -= whole_ecc_bytes(len);
     435                 :            :         }
     436                 :            : 
     437                 :          0 :         if (len) {
     438                 :          0 :                 bytes_wanted = BYTES_PER_ECC - len;
     439                 :            : 
     440                 :          0 :                 ecc_word.data = *src;
     441                 :          0 :                 memcpy(inc_uint64_by(&ecc_word.data, len), inc_ecc64_by(dst, len),
     442                 :            :                                 bytes_wanted);
     443                 :          0 :                 ecc_word.ecc = eccgenerate(be64_to_cpu(ecc_word.data));
     444                 :            : 
     445                 :          0 :                 *dst = ecc_word;
     446                 :            :         }
     447                 :            : 
     448                 :            :         return 0;
     449                 :            : }

Generated by: LCOV version 1.14