├── .github └── workflows │ └── github-deploy.yml ├── .gitignore ├── COPYING ├── MANIFEST.in ├── README.md ├── setup.py ├── src ├── bch.c ├── bch.h └── bchlib.c └── tests ├── __init__.py ├── test_chaining.py ├── test_consecutive.py ├── test_exercise.py └── test_exynos.py /.github/workflows/github-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and upload to PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | release: 10 | types: 11 | - published 12 | 13 | jobs: 14 | build_wheels: 15 | name: Build wheels on ${{ matrix.os }} 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest, macos-13, macos-14] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - uses: actions/setup-python@v5 25 | 26 | - name: Install cibuildwheel 27 | run: python -m pip install cibuildwheel==2.19.1 28 | 29 | - name: Build wheels 30 | run: python -m cibuildwheel --output-dir wheelhouse 31 | 32 | - uses: actions/upload-artifact@v3 33 | with: 34 | name: artifact 35 | path: ./wheelhouse/*.whl 36 | 37 | build_sdist: 38 | name: Build source distribution 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | 43 | - name: Build sdist 44 | run: pipx run build --sdist 45 | 46 | - uses: actions/upload-artifact@v3 47 | with: 48 | name: artifact 49 | path: dist/*.tar.gz 50 | 51 | upload_pypi: 52 | needs: [build_wheels, build_sdist] 53 | runs-on: ubuntu-latest 54 | environment: pypi 55 | permissions: 56 | id-token: write 57 | if: github.event_name == 'release' && github.event.action == 'published' 58 | steps: 59 | - uses: actions/download-artifact@v3 60 | with: 61 | name: artifact 62 | path: dist 63 | 64 | - uses: pypa/gh-action-pypi-publish@release/v1 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eggs 2 | *.egg-info/ 3 | .vscode/ 4 | __pycache__/ 5 | build/ 6 | dist/ 7 | *.so 8 | .cproject 9 | .project 10 | .pydevproject 11 | .pytest_cache 12 | .settings 13 | MANIFEST 14 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING README.md 2 | graft src -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-bchlib 2 | ============= 3 | 4 | This is a python module for encoding, decoding, and correcting data using [BCH codes](https://en.wikipedia.org/wiki/BCH_code). 5 | 6 | ## Requirements 7 | Python 3.6 or greater required. 8 | 9 | ## Installing the latest release: 10 | $ pip install bchlib 11 | 12 | ## Installing from source: 13 | On Linux, you will need python-dev or equivalent package. Windows, you need Microsoft Visual C++. I've tested this manually using [Visual Studio 2015](https://stackoverflow.com/a/44290942/6844002). 14 | 15 | $ pip install . 16 | 17 | ## Usage Example 18 | 19 | See Python's built-in help `import bchlib; help(bchlib)` and the module's [tests](tests) for usage examples. 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | 5 | from setuptools import Extension, setup 6 | 7 | NAME = 'bchlib' 8 | VERSION = '2.1.3' 9 | DESCRIPTION = 'A python wrapper module for the Linux kernel BCH library.' 10 | URL = 'https://github.com/jkent/python-bchlib' 11 | EMAIL = 'jeff@jkent.net' 12 | AUTHOR = 'Jeff Kent' 13 | REQUIRES_PYTHON = '>=3.6.0' 14 | 15 | BCHLIB_EXT_SRC = [ 16 | 'src/bch.c', 17 | 'src/bchlib.c', 18 | ] 19 | 20 | BCHLIB_EXT_DEP = [ 21 | 'src/bch.h', 22 | ] 23 | 24 | BCHLIB_EXT = Extension('bchlib', BCHLIB_EXT_SRC, depends=BCHLIB_EXT_DEP, 25 | extra_compile_args=['-std=c99']) 26 | 27 | here = os.path.abspath(os.path.dirname(__file__)) 28 | with open(os.path.join(here, 'README.md'), encoding='utf-8') as f: 29 | long_description = '\n' + f.read() 30 | 31 | setup( 32 | name=NAME, 33 | version=VERSION, 34 | description=DESCRIPTION, 35 | long_description=long_description, 36 | long_description_content_type='text/markdown', 37 | author=AUTHOR, 38 | author_email=EMAIL, 39 | python_requires=REQUIRES_PYTHON, 40 | url=URL, 41 | ext_modules=[BCHLIB_EXT], 42 | include_package_data=True, 43 | classifiers=[ 44 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 45 | 'Programming Language :: Python', 46 | 'Programming Language :: Python :: 3', 47 | 'Programming Language :: Python :: 3.6', 48 | 'Programming Language :: Python :: Implementation :: CPython', 49 | 'Programming Language :: Python :: Implementation :: PyPy', 50 | 'Intended Audience :: Developers', 51 | 'Topic :: Security :: Cryptography', 52 | ], 53 | ) 54 | -------------------------------------------------------------------------------- /src/bch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic binary BCH encoding/decoding library 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * this program; if not, write to the Free Software Foundation, Inc., 51 15 | * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 16 | * 17 | * Copyright © 2011 Parrot S.A. 18 | * 19 | * Author: Ivan Djelic 20 | * 21 | * Description: 22 | * 23 | * This library provides runtime configurable encoding/decoding of binary 24 | * Bose-Chaudhuri-Hocquenghem (BCH) codes. 25 | * 26 | * Call bch_init to get a pointer to a newly allocated bch_control structure for 27 | * the given m (Galois field order), t (error correction capability) and 28 | * (optional) primitive polynomial parameters. 29 | * 30 | * Call bch_encode to compute and store ecc parity bytes to a given buffer. 31 | * Call bch_decode to detect and locate errors in received data. 32 | * 33 | * On systems supporting hw BCH features, intermediate results may be provided 34 | * to bch_decode in order to skip certain steps. See bch_decode() documentation 35 | * for details. 36 | * 37 | * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of 38 | * parameters m and t; thus allowing extra compiler optimizations and providing 39 | * better (up to 2x) encoding performance. Using this option makes sense when 40 | * (m,t) are fixed and known in advance, e.g. when using BCH error correction 41 | * on a particular NAND flash device. 42 | * 43 | * Algorithmic details: 44 | * 45 | * Encoding is performed by processing 32 input bits in parallel, using 4 46 | * remainder lookup tables. 47 | * 48 | * The final stage of decoding involves the following internal steps: 49 | * a. Syndrome computation 50 | * b. Error locator polynomial computation using Berlekamp-Massey algorithm 51 | * c. Error locator root finding (by far the most expensive step) 52 | * 53 | * In this implementation, step c is not performed using the usual Chien search. 54 | * Instead, an alternative approach described in [1] is used. It consists in 55 | * factoring the error locator polynomial using the Berlekamp Trace algorithm 56 | * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial 57 | * solving techniques [2] are used. The resulting algorithm, called BTZ, yields 58 | * much better performance than Chien search for usual (m,t) values (typically 59 | * m >= 13, t < 32, see [1]). 60 | * 61 | * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields 62 | * of characteristic 2, in: Western European Workshop on Research in Cryptology 63 | * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. 64 | * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over 65 | * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. 66 | */ 67 | 68 | #if defined(__KERNEL__) 69 | 70 | # include 71 | # include 72 | # include 73 | # include 74 | # include 75 | # include 76 | # include 77 | # include 78 | 79 | #else 80 | 81 | # define _BSD_SOURCE 82 | # define _DEFAULT_SOURCE 83 | # if (defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) 84 | # include 85 | # define alloca(size) _alloca(size) 86 | # pragma comment( lib, "ws2_32.lib") 87 | # elif defined(__APPLE__) 88 | # include 89 | # define htobe32(x) OSSwapHostToBigInt32(x) 90 | # else 91 | # include 92 | # endif 93 | # if !defined(htobe32) 94 | # if BYTE_ORDER == LITTLE_ENDIAN 95 | # define htobe32(x) htonl(x) 96 | # elif BYTE_ORDER == BIG_ENDIAN 97 | # define htobe32(x) (x) 98 | # endif 99 | # endif 100 | # include 101 | # include 102 | # include 103 | # include 104 | # include 105 | # include "bch.h" 106 | # define MODULE_LICENSE(s) 107 | # define MODULE_AUTHOR(s) 108 | # define MODULE_DESCRIPTION(s) 109 | # define EXPORT_SYMBOL_GPL(x) 110 | # define GFP_KERNEL 111 | # define kmalloc(size, flags) malloc(size) 112 | # define kzalloc(size, flags) memset(malloc(size), 0, size) 113 | # define kfree(ptr) free(ptr) 114 | # define DIV_ROUND_UP(a, b) ((a + b - 1) / b) 115 | # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) 116 | # define cpu_to_be32(x) htobe32(x) 117 | # define WARN_ON(x) (x) 118 | #endif 119 | 120 | #if defined(CONFIG_BCH_CONST_PARAMS) 121 | #define GF_M(_p) (CONFIG_BCH_CONST_M) 122 | #define GF_T(_p) (CONFIG_BCH_CONST_T) 123 | #define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) 124 | #define BCH_MAX_M (CONFIG_BCH_CONST_M) 125 | #define BCH_MAX_T (CONFIG_BCH_CONST_T) 126 | #else 127 | #define GF_M(_p) ((_p)->m) 128 | #define GF_T(_p) ((_p)->t) 129 | #define GF_N(_p) ((_p)->n) 130 | #define BCH_MAX_M 31 /* 2KB */ 131 | #define BCH_MAX_T 64 /* 64 bit correction */ 132 | #endif 133 | 134 | #define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) 135 | #define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) 136 | 137 | #define BCH_ECC_MAX_WORDS DIV_ROUND_UP(BCH_MAX_M * BCH_MAX_T, 32) 138 | 139 | #ifndef dbg 140 | #define dbg(_fmt, ...) do {} while (0) 141 | #endif 142 | 143 | /* 144 | * represent a polynomial over GF(2^m) 145 | */ 146 | struct gf_poly { 147 | unsigned int deg; /* polynomial degree */ 148 | unsigned int c[]; /* polynomial terms */ 149 | }; 150 | 151 | /* given its degree, compute a polynomial size in bytes */ 152 | #define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) 153 | 154 | /* polynomial of degree 1 */ 155 | struct gf_poly_deg1 { 156 | struct { 157 | unsigned int deg; 158 | } poly; 159 | unsigned int c[2]; 160 | }; 161 | 162 | static uint8_t swap_bits_table[] = { 163 | 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 164 | 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 165 | 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 166 | 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 167 | 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 168 | 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 169 | 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 170 | 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 171 | 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 172 | 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 173 | 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 174 | 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 175 | 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 176 | 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 177 | 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 178 | 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 179 | 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 180 | 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 181 | 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 182 | 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 183 | 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 184 | 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 185 | 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 186 | 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 187 | 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 188 | 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 189 | 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 190 | 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 191 | 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 192 | 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 193 | 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 194 | 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, 195 | }; 196 | 197 | #if !defined(__APPLE__) 198 | static inline unsigned int fls(unsigned int x) 199 | { 200 | unsigned int count = 0; 201 | 202 | while (x >>= 1) { 203 | count++; 204 | } 205 | return count + 1; 206 | } 207 | #endif 208 | 209 | static uint8_t swap_bits(struct bch_control *bch, uint8_t in) 210 | { 211 | if (!bch->swap_bits) 212 | return in; 213 | 214 | return swap_bits_table[in]; 215 | } 216 | 217 | /* 218 | * same as bch_encode(), but process input data one byte at a time 219 | */ 220 | static void bch_encode_unaligned(struct bch_control *bch, 221 | const unsigned char *data, unsigned int len, 222 | uint32_t *ecc) 223 | { 224 | int i; 225 | const uint32_t *p; 226 | const int l = BCH_ECC_WORDS(bch)-1; 227 | 228 | while (len--) { 229 | uint8_t tmp = swap_bits(bch, *data++); 230 | 231 | p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(tmp)) & 0xff); 232 | 233 | for (i = 0; i < l; i++) 234 | ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); 235 | 236 | ecc[l] = (ecc[l] << 8)^(*p); 237 | } 238 | } 239 | 240 | /* 241 | * convert ecc bytes to aligned, zero-padded 32-bit ecc words 242 | */ 243 | static void load_ecc8(struct bch_control *bch, uint32_t *dst, 244 | const uint8_t *src) 245 | { 246 | uint8_t pad[4] = {0, 0, 0, 0}; 247 | unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; 248 | 249 | for (i = 0; i < nwords; i++, src += 4) 250 | dst[i] = ((uint32_t)swap_bits(bch, src[0]) << 24) | 251 | ((uint32_t)swap_bits(bch, src[1]) << 16) | 252 | ((uint32_t)swap_bits(bch, src[2]) << 8) | 253 | swap_bits(bch, src[3]); 254 | 255 | memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); 256 | dst[nwords] = ((uint32_t)swap_bits(bch, pad[0]) << 24) | 257 | ((uint32_t)swap_bits(bch, pad[1]) << 16) | 258 | ((uint32_t)swap_bits(bch, pad[2]) << 8) | 259 | swap_bits(bch, pad[3]); 260 | } 261 | 262 | /* 263 | * convert 32-bit ecc words to ecc bytes 264 | */ 265 | static void store_ecc8(struct bch_control *bch, uint8_t *dst, 266 | const uint32_t *src) 267 | { 268 | uint8_t pad[4]; 269 | unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; 270 | 271 | for (i = 0; i < nwords; i++) { 272 | *dst++ = swap_bits(bch, src[i] >> 24); 273 | *dst++ = swap_bits(bch, src[i] >> 16); 274 | *dst++ = swap_bits(bch, src[i] >> 8); 275 | *dst++ = swap_bits(bch, src[i]); 276 | } 277 | pad[0] = swap_bits(bch, src[nwords] >> 24); 278 | pad[1] = swap_bits(bch, src[nwords] >> 16); 279 | pad[2] = swap_bits(bch, src[nwords] >> 8); 280 | pad[3] = swap_bits(bch, src[nwords]); 281 | memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); 282 | } 283 | 284 | /** 285 | * bch_encode - calculate BCH ecc parity of data 286 | * @bch: BCH control structure 287 | * @data: data to encode 288 | * @len: data length in bytes 289 | * @ecc: ecc parity data, must be initialized by caller 290 | * 291 | * The @ecc parity array is used both as input and output parameter, in order to 292 | * allow incremental computations. It should be of the size indicated by member 293 | * @ecc_bytes of @bch, and should be initialized to 0 before the first call. 294 | * 295 | * The exact number of computed ecc parity bits is given by member @ecc_bits of 296 | * @bch; it may be less than m*t for large values of t. 297 | */ 298 | void bch_encode(struct bch_control *bch, const uint8_t *data, 299 | unsigned int len, uint8_t *ecc) 300 | { 301 | const unsigned int l = BCH_ECC_WORDS(bch)-1; 302 | unsigned int i, mlen; 303 | unsigned long m; 304 | uint32_t w, r[BCH_ECC_MAX_WORDS]; 305 | const size_t r_bytes = BCH_ECC_WORDS(bch) * sizeof(*r); 306 | const uint32_t * const tab0 = bch->mod8_tab; 307 | const uint32_t * const tab1 = tab0 + 256*(l+1); 308 | const uint32_t * const tab2 = tab1 + 256*(l+1); 309 | const uint32_t * const tab3 = tab2 + 256*(l+1); 310 | const uint32_t *pdata, *p0, *p1, *p2, *p3; 311 | 312 | if (WARN_ON(r_bytes > sizeof(r))) 313 | return; 314 | 315 | if (ecc) { 316 | /* load ecc parity bytes into internal 32-bit buffer */ 317 | load_ecc8(bch, bch->ecc_buf, ecc); 318 | } else { 319 | memset(bch->ecc_buf, 0, r_bytes); 320 | } 321 | 322 | /* process first unaligned data bytes */ 323 | m = ((unsigned long)data) & 3; 324 | if (m) { 325 | mlen = (len < (4-m)) ? len : 4-m; 326 | bch_encode_unaligned(bch, data, mlen, bch->ecc_buf); 327 | data += mlen; 328 | len -= mlen; 329 | } 330 | 331 | /* process 32-bit aligned data words */ 332 | pdata = (uint32_t *)data; 333 | mlen = len/4; 334 | data += 4*mlen; 335 | len -= 4*mlen; 336 | memcpy(r, bch->ecc_buf, r_bytes); 337 | 338 | /* 339 | * split each 32-bit word into 4 polynomials of weight 8 as follows: 340 | * 341 | * 31 ...24 23 ...16 15 ... 8 7 ... 0 342 | * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt 343 | * tttttttt mod g = r0 (precomputed) 344 | * zzzzzzzz 00000000 mod g = r1 (precomputed) 345 | * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) 346 | * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) 347 | * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 348 | */ 349 | while (mlen--) { 350 | /* input data is read in big-endian format */ 351 | w = cpu_to_be32(*pdata++); 352 | if (bch->swap_bits) 353 | w = (uint32_t)swap_bits(bch, w) | 354 | ((uint32_t)swap_bits(bch, w >> 8) << 8) | 355 | ((uint32_t)swap_bits(bch, w >> 16) << 16) | 356 | ((uint32_t)swap_bits(bch, w >> 24) << 24); 357 | w ^= r[0]; 358 | p0 = tab0 + (l+1)*((w >> 0) & 0xff); 359 | p1 = tab1 + (l+1)*((w >> 8) & 0xff); 360 | p2 = tab2 + (l+1)*((w >> 16) & 0xff); 361 | p3 = tab3 + (l+1)*((w >> 24) & 0xff); 362 | 363 | for (i = 0; i < l; i++) 364 | r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; 365 | 366 | r[l] = p0[l]^p1[l]^p2[l]^p3[l]; 367 | } 368 | memcpy(bch->ecc_buf, r, r_bytes); 369 | 370 | /* process last unaligned bytes */ 371 | if (len) 372 | bch_encode_unaligned(bch, data, len, bch->ecc_buf); 373 | 374 | /* store ecc parity bytes into original parity buffer */ 375 | if (ecc) 376 | store_ecc8(bch, ecc, bch->ecc_buf); 377 | } 378 | EXPORT_SYMBOL_GPL(bch_encode); 379 | 380 | static inline int modulo(struct bch_control *bch, unsigned int v) 381 | { 382 | const unsigned int n = GF_N(bch); 383 | while (v >= n) { 384 | v -= n; 385 | v = (v & n) + (v >> GF_M(bch)); 386 | } 387 | return v; 388 | } 389 | 390 | /* 391 | * shorter and faster modulo function, only works when v < 2N. 392 | */ 393 | static inline int mod_s(struct bch_control *bch, unsigned int v) 394 | { 395 | const unsigned int n = GF_N(bch); 396 | return (v < n) ? v : v-n; 397 | } 398 | 399 | static inline int deg(unsigned int poly) 400 | { 401 | /* polynomial degree is the most-significant bit index */ 402 | return fls(poly)-1; 403 | } 404 | 405 | static inline int parity(unsigned int x) 406 | { 407 | /* 408 | * public domain code snippet, lifted from 409 | * http://www-graphics.stanford.edu/~seander/bithacks.html 410 | */ 411 | x ^= x >> 1; 412 | x ^= x >> 2; 413 | x = (x & 0x11111111U) * 0x11111111U; 414 | return (x >> 28) & 1; 415 | } 416 | 417 | /* Galois field basic operations: multiply, divide, inverse, etc. */ 418 | 419 | static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, 420 | unsigned int b) 421 | { 422 | return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ 423 | bch->a_log_tab[b])] : 0; 424 | } 425 | 426 | static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) 427 | { 428 | return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; 429 | } 430 | 431 | static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, 432 | unsigned int b) 433 | { 434 | return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ 435 | GF_N(bch)-bch->a_log_tab[b])] : 0; 436 | } 437 | 438 | static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) 439 | { 440 | return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; 441 | } 442 | 443 | static inline unsigned int a_pow(struct bch_control *bch, int i) 444 | { 445 | return bch->a_pow_tab[modulo(bch, i)]; 446 | } 447 | 448 | static inline int a_log(struct bch_control *bch, unsigned int x) 449 | { 450 | return bch->a_log_tab[x]; 451 | } 452 | 453 | static inline int a_ilog(struct bch_control *bch, unsigned int x) 454 | { 455 | return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); 456 | } 457 | 458 | void bch_compute_even_syndromes(struct bch_control *bch, unsigned int *syn) 459 | { 460 | int i; 461 | const int t = GF_T(bch); 462 | 463 | /* v(a^(2j)) = v(a^j)^2 */ 464 | for (i = 0; i < t; i++) 465 | syn[2*i+1] = gf_sqr(bch, syn[i]); 466 | } 467 | EXPORT_SYMBOL_GPL(bch_compute_even_syndromes); 468 | 469 | /* 470 | * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t 471 | */ 472 | static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, 473 | unsigned int *syn) 474 | { 475 | int i, j, s; 476 | unsigned int m; 477 | uint32_t poly; 478 | const int t = GF_T(bch); 479 | 480 | s = bch->ecc_bits; 481 | 482 | /* make sure extra bits in last ecc word are cleared */ 483 | m = ((unsigned int)s) & 31; 484 | if (m) 485 | ecc[s/32] &= ~((1u << (32-m))-1); 486 | memset(syn, 0, 2*t*sizeof(*syn)); 487 | 488 | /* compute v(a^j) for j=1 .. 2t-1 */ 489 | do { 490 | poly = *ecc++; 491 | s -= 32; 492 | while (poly) { 493 | i = deg(poly); 494 | for (j = 0; j < 2*t; j += 2) 495 | syn[j] ^= a_pow(bch, (j+1)*(i+s)); 496 | 497 | poly ^= (1 << i); 498 | } 499 | } while (s > 0); 500 | 501 | bch_compute_even_syndromes(bch, syn); 502 | } 503 | 504 | static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) 505 | { 506 | memcpy(dst, src, GF_POLY_SZ(src->deg)); 507 | } 508 | 509 | static int compute_error_locator_polynomial(struct bch_control *bch, 510 | const unsigned int *syn) 511 | { 512 | const unsigned int t = GF_T(bch); 513 | const unsigned int n = GF_N(bch); 514 | unsigned int i, j, tmp, l, pd = 1, d = syn[0]; 515 | struct gf_poly *elp = bch->elp; 516 | struct gf_poly *pelp = bch->poly_2t[0]; 517 | struct gf_poly *elp_copy = bch->poly_2t[1]; 518 | int k, pp = -1; 519 | 520 | memset(pelp, 0, GF_POLY_SZ(2*t)); 521 | memset(elp, 0, GF_POLY_SZ(2*t)); 522 | 523 | pelp->deg = 0; 524 | pelp->c[0] = 1; 525 | elp->deg = 0; 526 | elp->c[0] = 1; 527 | 528 | /* use simplified binary Berlekamp-Massey algorithm */ 529 | for (i = 0; (i < t) && (elp->deg <= t); i++) { 530 | if (d) { 531 | k = 2*i-pp; 532 | gf_poly_copy(elp_copy, elp); 533 | /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ 534 | tmp = a_log(bch, d)+n-a_log(bch, pd); 535 | for (j = 0; j <= pelp->deg; j++) { 536 | if (pelp->c[j]) { 537 | l = a_log(bch, pelp->c[j]); 538 | elp->c[j+k] ^= a_pow(bch, tmp+l); 539 | } 540 | } 541 | /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ 542 | tmp = pelp->deg+k; 543 | if (tmp > elp->deg) { 544 | elp->deg = tmp; 545 | gf_poly_copy(pelp, elp_copy); 546 | pd = d; 547 | pp = 2*i; 548 | } 549 | } 550 | /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ 551 | if (i < t-1) { 552 | d = syn[2*i+2]; 553 | for (j = 1; j <= elp->deg; j++) 554 | d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); 555 | } 556 | } 557 | dbg("elp=%s\n", gf_poly_str(elp)); 558 | return (elp->deg > t) ? -1 : (int)elp->deg; 559 | } 560 | 561 | /* 562 | * solve a m x m linear system in GF(2) with an expected number of solutions, 563 | * and return the number of found solutions 564 | */ 565 | static int solve_linear_system(struct bch_control *bch, unsigned int *rows, 566 | unsigned int *sol, int nsol) 567 | { 568 | const int m = GF_M(bch); 569 | unsigned int tmp, mask; 570 | int rem, c, r, p, k, param[BCH_MAX_M]; 571 | 572 | k = 0; 573 | mask = 1 << m; 574 | 575 | /* Gaussian elimination */ 576 | for (c = 0; c < m; c++) { 577 | rem = 0; 578 | p = c-k; 579 | /* find suitable row for elimination */ 580 | for (r = p; r < m; r++) { 581 | if (rows[r] & mask) { 582 | if (r != p) { 583 | tmp = rows[r]; 584 | rows[r] = rows[p]; 585 | rows[p] = tmp; 586 | } 587 | rem = r+1; 588 | break; 589 | } 590 | } 591 | if (rem) { 592 | /* perform elimination on remaining rows */ 593 | tmp = rows[p]; 594 | for (r = rem; r < m; r++) { 595 | if (rows[r] & mask) 596 | rows[r] ^= tmp; 597 | } 598 | } else { 599 | /* elimination not needed, store defective row index */ 600 | param[k++] = c; 601 | } 602 | mask >>= 1; 603 | } 604 | /* rewrite system, inserting fake parameter rows */ 605 | if (k > 0) { 606 | p = k; 607 | for (r = m-1; r >= 0; r--) { 608 | if ((r > m-1-k) && rows[r]) 609 | /* system has no solution */ 610 | return 0; 611 | 612 | rows[r] = (p && (r == param[p-1])) ? 613 | p--, 1u << (m-r) : rows[r-p]; 614 | } 615 | } 616 | 617 | if (nsol != (1 << k)) 618 | /* unexpected number of solutions */ 619 | return 0; 620 | 621 | for (p = 0; p < nsol; p++) { 622 | /* set parameters for p-th solution */ 623 | for (c = 0; c < k; c++) 624 | rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); 625 | 626 | /* compute unique solution */ 627 | tmp = 0; 628 | for (r = m-1; r >= 0; r--) { 629 | mask = rows[r] & (tmp|1); 630 | tmp |= parity(mask) << (m-r); 631 | } 632 | sol[p] = tmp >> 1; 633 | } 634 | return nsol; 635 | } 636 | 637 | /* 638 | * this function builds and solves a linear system for finding roots of a degree 639 | * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). 640 | */ 641 | static int find_affine4_roots(struct bch_control *bch, unsigned int a, 642 | unsigned int b, unsigned int c, 643 | unsigned int *roots) 644 | { 645 | int i, j, k; 646 | const int m = GF_M(bch); 647 | unsigned int mask = 0xffff, t, rows[32] = {0,}; 648 | 649 | j = a_log(bch, b); 650 | k = a_log(bch, a); 651 | rows[0] = c; 652 | 653 | /* build linear system to solve X^4+aX^2+bX+c = 0 */ 654 | for (i = 0; i < m; i++) { 655 | rows[i+1] = bch->a_pow_tab[4*i]^ 656 | (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ 657 | (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); 658 | j++; 659 | k += 2; 660 | } 661 | /* 662 | * transpose 32x32 matrix before passing it to linear solver 663 | * warning: this code assumes m < 32 664 | */ 665 | for (j = 16; j != 0; j >>= 1, mask ^= (mask << j)) { 666 | for (k = 0; k < 16; k = (k+j+1) & ~j) { 667 | t = ((rows[k] >> j)^rows[k+j]) & mask; 668 | rows[k] ^= (t << j); 669 | rows[k+j] ^= t; 670 | } 671 | } 672 | return solve_linear_system(bch, rows, roots, 4); 673 | } 674 | 675 | /* 676 | * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) 677 | */ 678 | static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, 679 | unsigned int *roots) 680 | { 681 | int n = 0; 682 | 683 | if (poly->c[0]) 684 | /* poly[X] = bX+c with c!=0, root=c/b */ 685 | roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ 686 | bch->a_log_tab[poly->c[1]]); 687 | return n; 688 | } 689 | 690 | /* 691 | * compute roots of a degree 2 polynomial over GF(2^m) 692 | */ 693 | static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, 694 | unsigned int *roots) 695 | { 696 | int n = 0, i, l0, l1, l2; 697 | unsigned int u, v, r; 698 | 699 | if (poly->c[0] && poly->c[1]) { 700 | 701 | l0 = bch->a_log_tab[poly->c[0]]; 702 | l1 = bch->a_log_tab[poly->c[1]]; 703 | l2 = bch->a_log_tab[poly->c[2]]; 704 | 705 | /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ 706 | u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); 707 | /* 708 | * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): 709 | * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = 710 | * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) 711 | * i.e. r and r+1 are roots iff Tr(u)=0 712 | */ 713 | r = 0; 714 | v = u; 715 | while (v) { 716 | i = deg(v); 717 | r ^= bch->xi_tab[i]; 718 | v ^= (1 << i); 719 | } 720 | /* verify root */ 721 | if ((gf_sqr(bch, r)^r) == u) { 722 | /* reverse z=a/bX transformation and compute log(1/r) */ 723 | roots[n++] = modulo(bch, 2*GF_N(bch)-l1- 724 | bch->a_log_tab[r]+l2); 725 | roots[n++] = modulo(bch, 2*GF_N(bch)-l1- 726 | bch->a_log_tab[r^1]+l2); 727 | } 728 | } 729 | return n; 730 | } 731 | 732 | /* 733 | * compute roots of a degree 3 polynomial over GF(2^m) 734 | */ 735 | static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, 736 | unsigned int *roots) 737 | { 738 | int i, n = 0; 739 | unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; 740 | 741 | if (poly->c[0]) { 742 | /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ 743 | e3 = poly->c[3]; 744 | c2 = gf_div(bch, poly->c[0], e3); 745 | b2 = gf_div(bch, poly->c[1], e3); 746 | a2 = gf_div(bch, poly->c[2], e3); 747 | 748 | /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ 749 | c = gf_mul(bch, a2, c2); /* c = a2c2 */ 750 | b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ 751 | a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ 752 | 753 | /* find the 4 roots of this affine polynomial */ 754 | if (find_affine4_roots(bch, a, b, c, tmp) == 4) { 755 | /* remove a2 from final list of roots */ 756 | for (i = 0; i < 4; i++) { 757 | if (tmp[i] != a2) 758 | roots[n++] = a_ilog(bch, tmp[i]); 759 | } 760 | } 761 | } 762 | return n; 763 | } 764 | 765 | /* 766 | * compute roots of a degree 4 polynomial over GF(2^m) 767 | */ 768 | static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, 769 | unsigned int *roots) 770 | { 771 | int i, l, n = 0; 772 | unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; 773 | 774 | if (poly->c[0] == 0) 775 | return 0; 776 | 777 | /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ 778 | e4 = poly->c[4]; 779 | d = gf_div(bch, poly->c[0], e4); 780 | c = gf_div(bch, poly->c[1], e4); 781 | b = gf_div(bch, poly->c[2], e4); 782 | a = gf_div(bch, poly->c[3], e4); 783 | 784 | /* use Y=1/X transformation to get an affine polynomial */ 785 | if (a) { 786 | /* first, eliminate cX by using z=X+e with ae^2+c=0 */ 787 | if (c) { 788 | /* compute e such that e^2 = c/a */ 789 | f = gf_div(bch, c, a); 790 | l = a_log(bch, f); 791 | l += (l & 1) ? GF_N(bch) : 0; 792 | e = a_pow(bch, l/2); 793 | /* 794 | * use transformation z=X+e: 795 | * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d 796 | * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d 797 | * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d 798 | * z^4 + az^3 + b'z^2 + d' 799 | */ 800 | d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; 801 | b = gf_mul(bch, a, e)^b; 802 | } 803 | /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ 804 | if (d == 0) 805 | /* assume all roots have multiplicity 1 */ 806 | return 0; 807 | 808 | c2 = gf_inv(bch, d); 809 | b2 = gf_div(bch, a, d); 810 | a2 = gf_div(bch, b, d); 811 | } else { 812 | /* polynomial is already affine */ 813 | c2 = d; 814 | b2 = c; 815 | a2 = b; 816 | } 817 | /* find the 4 roots of this affine polynomial */ 818 | if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { 819 | for (i = 0; i < 4; i++) { 820 | /* post-process roots (reverse transformations) */ 821 | f = a ? gf_inv(bch, roots[i]) : roots[i]; 822 | roots[i] = a_ilog(bch, f^e); 823 | } 824 | n = 4; 825 | } 826 | return n; 827 | } 828 | 829 | /* 830 | * build monic, log-based representation of a polynomial 831 | */ 832 | static void gf_poly_logrep(struct bch_control *bch, 833 | const struct gf_poly *a, int *rep) 834 | { 835 | int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); 836 | 837 | /* represent 0 values with -1; warning, rep[d] is not set to 1 */ 838 | for (i = 0; i < d; i++) 839 | rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; 840 | } 841 | 842 | /* 843 | * compute polynomial Euclidean division remainder in GF(2^m)[X] 844 | */ 845 | static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, 846 | const struct gf_poly *b, int *rep) 847 | { 848 | int la, p, m; 849 | unsigned int i, j, *c = a->c; 850 | const unsigned int d = b->deg; 851 | 852 | if (a->deg < d) 853 | return; 854 | 855 | /* reuse or compute log representation of denominator */ 856 | if (!rep) { 857 | rep = bch->cache; 858 | gf_poly_logrep(bch, b, rep); 859 | } 860 | 861 | for (j = a->deg; j >= d; j--) { 862 | if (c[j]) { 863 | la = a_log(bch, c[j]); 864 | p = j-d; 865 | for (i = 0; i < d; i++, p++) { 866 | m = rep[i]; 867 | if (m >= 0) 868 | c[p] ^= bch->a_pow_tab[mod_s(bch, 869 | m+la)]; 870 | } 871 | } 872 | } 873 | a->deg = d-1; 874 | while (!c[a->deg] && a->deg) 875 | a->deg--; 876 | } 877 | 878 | /* 879 | * compute polynomial Euclidean division quotient in GF(2^m)[X] 880 | */ 881 | static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, 882 | const struct gf_poly *b, struct gf_poly *q) 883 | { 884 | if (a->deg >= b->deg) { 885 | q->deg = a->deg-b->deg; 886 | /* compute a mod b (modifies a) */ 887 | gf_poly_mod(bch, a, b, NULL); 888 | /* quotient is stored in upper part of polynomial a */ 889 | memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); 890 | } else { 891 | q->deg = 0; 892 | q->c[0] = 0; 893 | } 894 | } 895 | 896 | /* 897 | * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] 898 | */ 899 | static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, 900 | struct gf_poly *b) 901 | { 902 | struct gf_poly *tmp; 903 | 904 | dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); 905 | 906 | if (a->deg < b->deg) { 907 | tmp = b; 908 | b = a; 909 | a = tmp; 910 | } 911 | 912 | while (b->deg > 0) { 913 | gf_poly_mod(bch, a, b, NULL); 914 | tmp = b; 915 | b = a; 916 | a = tmp; 917 | } 918 | 919 | dbg("%s\n", gf_poly_str(a)); 920 | 921 | return a; 922 | } 923 | 924 | /* 925 | * Given a polynomial f and an integer k, compute Tr(a^kX) mod f 926 | * This is used in Berlekamp Trace algorithm for splitting polynomials 927 | */ 928 | static void compute_trace_bk_mod(struct bch_control *bch, int k, 929 | const struct gf_poly *f, struct gf_poly *z, 930 | struct gf_poly *out) 931 | { 932 | const int m = GF_M(bch); 933 | int i, j; 934 | 935 | /* z contains z^2j mod f */ 936 | z->deg = 1; 937 | z->c[0] = 0; 938 | z->c[1] = bch->a_pow_tab[k]; 939 | 940 | out->deg = 0; 941 | memset(out, 0, GF_POLY_SZ(f->deg)); 942 | 943 | /* compute f log representation only once */ 944 | gf_poly_logrep(bch, f, bch->cache); 945 | 946 | for (i = 0; i < m; i++) { 947 | /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ 948 | for (j = z->deg; j >= 0; j--) { 949 | out->c[j] ^= z->c[j]; 950 | z->c[2*j] = gf_sqr(bch, z->c[j]); 951 | z->c[2*j+1] = 0; 952 | } 953 | if (z->deg > out->deg) 954 | out->deg = z->deg; 955 | 956 | if (i < m-1) { 957 | z->deg *= 2; 958 | /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ 959 | gf_poly_mod(bch, z, f, bch->cache); 960 | } 961 | } 962 | while (!out->c[out->deg] && out->deg) 963 | out->deg--; 964 | 965 | dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); 966 | } 967 | 968 | /* 969 | * factor a polynomial using Berlekamp Trace algorithm (BTA) 970 | */ 971 | static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, 972 | struct gf_poly **g, struct gf_poly **h) 973 | { 974 | struct gf_poly *f2 = bch->poly_2t[0]; 975 | struct gf_poly *q = bch->poly_2t[1]; 976 | struct gf_poly *tk = bch->poly_2t[2]; 977 | struct gf_poly *z = bch->poly_2t[3]; 978 | struct gf_poly *gcd; 979 | 980 | dbg("factoring %s...\n", gf_poly_str(f)); 981 | 982 | *g = f; 983 | *h = NULL; 984 | 985 | /* tk = Tr(a^k.X) mod f */ 986 | compute_trace_bk_mod(bch, k, f, z, tk); 987 | 988 | if (tk->deg > 0) { 989 | /* compute g = gcd(f, tk) (destructive operation) */ 990 | gf_poly_copy(f2, f); 991 | gcd = gf_poly_gcd(bch, f2, tk); 992 | if (gcd->deg < f->deg) { 993 | /* compute h=f/gcd(f,tk); this will modify f and q */ 994 | gf_poly_div(bch, f, gcd, q); 995 | /* store g and h in-place (clobbering f) */ 996 | *h = (struct gf_poly *) &((struct gf_poly_deg1 *)f)[gcd->deg].poly; 997 | gf_poly_copy(*g, gcd); 998 | gf_poly_copy(*h, q); 999 | } 1000 | } 1001 | } 1002 | 1003 | /* 1004 | * find roots of a polynomial, using BTZ algorithm; see the beginning of this 1005 | * file for details 1006 | */ 1007 | static int find_poly_roots(struct bch_control *bch, unsigned int k, 1008 | struct gf_poly *poly, unsigned int *roots) 1009 | { 1010 | int cnt; 1011 | struct gf_poly *f1, *f2; 1012 | 1013 | switch (poly->deg) { 1014 | /* handle low degree polynomials with ad hoc techniques */ 1015 | case 1: 1016 | cnt = find_poly_deg1_roots(bch, poly, roots); 1017 | break; 1018 | case 2: 1019 | cnt = find_poly_deg2_roots(bch, poly, roots); 1020 | break; 1021 | case 3: 1022 | cnt = find_poly_deg3_roots(bch, poly, roots); 1023 | break; 1024 | case 4: 1025 | cnt = find_poly_deg4_roots(bch, poly, roots); 1026 | break; 1027 | default: 1028 | /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ 1029 | cnt = 0; 1030 | if (poly->deg && (k <= GF_M(bch))) { 1031 | factor_polynomial(bch, k, poly, &f1, &f2); 1032 | if (f1) 1033 | cnt += find_poly_roots(bch, k+1, f1, roots); 1034 | if (f2) 1035 | cnt += find_poly_roots(bch, k+1, f2, roots+cnt); 1036 | } 1037 | break; 1038 | } 1039 | return cnt; 1040 | } 1041 | 1042 | #if defined(USE_CHIEN_SEARCH) 1043 | /* 1044 | * exhaustive root search (Chien) implementation - not used, included only for 1045 | * reference/comparison tests 1046 | */ 1047 | static int chien_search(struct bch_control *bch, unsigned int len, 1048 | struct gf_poly *p, unsigned int *roots) 1049 | { 1050 | int m; 1051 | unsigned int i, j, syn, syn0, count = 0; 1052 | const unsigned int k = 8*len+bch->ecc_bits; 1053 | 1054 | /* use a log-based representation of polynomial */ 1055 | gf_poly_logrep(bch, p, bch->cache); 1056 | bch->cache[p->deg] = 0; 1057 | syn0 = gf_div(bch, p->c[0], p->c[p->deg]); 1058 | 1059 | for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { 1060 | /* compute elp(a^i) */ 1061 | for (j = 1, syn = syn0; j <= p->deg; j++) { 1062 | m = bch->cache[j]; 1063 | if (m >= 0) 1064 | syn ^= a_pow(bch, m+j*i); 1065 | } 1066 | if (syn == 0) { 1067 | roots[count++] = GF_N(bch)-i; 1068 | if (count == p->deg) 1069 | break; 1070 | } 1071 | } 1072 | return (count == p->deg) ? count : 0; 1073 | } 1074 | #define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) 1075 | #endif /* USE_CHIEN_SEARCH */ 1076 | 1077 | /** 1078 | * bch_decode - decode received codeword and find bit error locations 1079 | * @bch: BCH control structure 1080 | * @data: received data, ignored if @calc_ecc is provided 1081 | * @len: data length in bytes, must always be provided 1082 | * @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc 1083 | * @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data 1084 | * @syn: hw computed syndrome data (if NULL, syndrome is calculated) 1085 | * @errloc: output array of error locations 1086 | * 1087 | * Returns: 1088 | * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if 1089 | * invalid parameters were provided 1090 | * 1091 | * Depending on the available hw BCH support and the need to compute @calc_ecc 1092 | * separately (using bch_encode()), this function should be called with one of 1093 | * the following parameter configurations - 1094 | * 1095 | * by providing @data and @recv_ecc only: 1096 | * bch_decode(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc) 1097 | * 1098 | * by providing @recv_ecc and @calc_ecc: 1099 | * bch_decode(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc) 1100 | * 1101 | * by providing ecc = recv_ecc XOR calc_ecc: 1102 | * bch_decode(@bch, NULL, @len, NULL, ecc, NULL, @errloc) 1103 | * 1104 | * by providing syndrome results @syn: 1105 | * bch_decode(@bch, NULL, @len, NULL, NULL, @syn, @errloc) 1106 | * 1107 | * Once bch_decode() has successfully returned with a positive value, error 1108 | * locations returned in array @errloc should be interpreted as follows - 1109 | * 1110 | * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for 1111 | * data correction) 1112 | * 1113 | * if (errloc[n] < 8*len), then n-th error is located in data and can be 1114 | * corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8); 1115 | * 1116 | * Note that this function does not perform any data correction by itself, it 1117 | * merely indicates error locations. 1118 | */ 1119 | int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len, 1120 | const uint8_t *recv_ecc, const uint8_t *calc_ecc, 1121 | const unsigned int *syn, unsigned int *errloc) 1122 | { 1123 | const unsigned int ecc_words = BCH_ECC_WORDS(bch); 1124 | unsigned int nbits; 1125 | int i, err, nroots; 1126 | uint32_t sum; 1127 | 1128 | /* sanity check: make sure data length can be handled */ 1129 | if (8*len > (bch->n-bch->ecc_bits)) 1130 | return -EINVAL; 1131 | 1132 | /* if caller does not provide syndromes, compute them */ 1133 | if (!syn) { 1134 | if (!calc_ecc) { 1135 | /* compute received data ecc into an internal buffer */ 1136 | if (!data || !recv_ecc) 1137 | return -EINVAL; 1138 | bch_encode(bch, data, len, NULL); 1139 | } else { 1140 | /* load provided calculated ecc */ 1141 | load_ecc8(bch, bch->ecc_buf, calc_ecc); 1142 | } 1143 | /* load received ecc or assume it was XORed in calc_ecc */ 1144 | if (recv_ecc) { 1145 | load_ecc8(bch, bch->ecc_buf2, recv_ecc); 1146 | /* XOR received and calculated ecc */ 1147 | for (i = 0, sum = 0; i < (int)ecc_words; i++) { 1148 | bch->ecc_buf[i] ^= bch->ecc_buf2[i]; 1149 | sum |= bch->ecc_buf[i]; 1150 | } 1151 | if (!sum) 1152 | /* no error found */ 1153 | return 0; 1154 | } 1155 | compute_syndromes(bch, bch->ecc_buf, bch->syn); 1156 | syn = bch->syn; 1157 | } 1158 | 1159 | err = compute_error_locator_polynomial(bch, syn); 1160 | if (err > 0) { 1161 | nroots = find_poly_roots(bch, 1, bch->elp, errloc); 1162 | if (err != nroots) 1163 | err = -1; 1164 | } 1165 | if (err > 0) { 1166 | /* post-process raw error locations for easier correction */ 1167 | nbits = (len*8)+bch->ecc_bits; 1168 | for (i = 0; i < err; i++) { 1169 | if (errloc[i] >= nbits) { 1170 | err = -1; 1171 | break; 1172 | } 1173 | errloc[i] = nbits-1-errloc[i]; 1174 | if (!bch->swap_bits) 1175 | errloc[i] = (errloc[i] & ~7) | 1176 | (7-(errloc[i] & 7)); 1177 | } 1178 | } 1179 | return (err >= 0) ? err : -EBADMSG; 1180 | } 1181 | EXPORT_SYMBOL_GPL(bch_decode); 1182 | 1183 | /* 1184 | * generate Galois field lookup tables 1185 | */ 1186 | static int build_gf_tables(struct bch_control *bch, unsigned int poly) 1187 | { 1188 | unsigned int i, x = 1; 1189 | const unsigned int k = 1 << deg(poly); 1190 | 1191 | /* primitive polynomial must be of degree m */ 1192 | if (k != (1u << GF_M(bch))) 1193 | return -1; 1194 | 1195 | for (i = 0; i < GF_N(bch); i++) { 1196 | bch->a_pow_tab[i] = x; 1197 | bch->a_log_tab[x] = i; 1198 | if (i && (x == 1)) 1199 | /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; 1206 | bch->a_log_tab[0] = 0; 1207 | 1208 | return 0; 1209 | } 1210 | 1211 | /* 1212 | * compute generator polynomial remainder tables for fast encoding 1213 | */ 1214 | static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) 1215 | { 1216 | int i, j, b, d; 1217 | uint32_t data, hi, lo, *tab; 1218 | const int l = BCH_ECC_WORDS(bch); 1219 | const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); 1220 | const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); 1221 | 1222 | memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); 1223 | 1224 | for (i = 0; i < 256; i++) { 1225 | /* p(X)=i is a small polynomial of weight <= 8 */ 1226 | for (b = 0; b < 4; b++) { 1227 | /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ 1228 | tab = bch->mod8_tab + (b*256+i)*l; 1229 | data = i << (8*b); 1230 | while (data) { 1231 | d = deg(data); 1232 | /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ 1233 | data ^= g[0] >> (31-d); 1234 | for (j = 0; j < ecclen; j++) { 1235 | hi = (d < 31) ? g[j] << (d+1) : 0; 1236 | lo = (j+1 < plen) ? 1237 | g[j+1] >> (31-d) : 0; 1238 | tab[j] ^= hi|lo; 1239 | } 1240 | } 1241 | } 1242 | } 1243 | } 1244 | 1245 | /* 1246 | * build a base for factoring degree 2 polynomials 1247 | */ 1248 | static int build_deg2_base(struct bch_control *bch) 1249 | { 1250 | const int m = GF_M(bch); 1251 | int i, j, r; 1252 | unsigned int sum, x, y, remaining, ak = 0, xi[BCH_MAX_M]; 1253 | 1254 | /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ 1255 | for (i = 0; i < m; i++) { 1256 | for (j = 0, sum = 0; j < m; j++) 1257 | sum ^= a_pow(bch, i*(1 << j)); 1258 | 1259 | if (sum) { 1260 | ak = bch->a_pow_tab[i]; 1261 | break; 1262 | } 1263 | } 1264 | /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ 1265 | remaining = m; 1266 | memset(xi, 0, sizeof(xi)); 1267 | 1268 | for (x = 0; (x <= GF_N(bch)) && remaining; x++) { 1269 | y = gf_sqr(bch, x)^x; 1270 | for (i = 0; i < 2; i++) { 1271 | r = a_log(bch, y); 1272 | if (y && (r < m) && !xi[r]) { 1273 | bch->xi_tab[r] = x; 1274 | xi[r] = 1; 1275 | remaining--; 1276 | dbg("x%d = %x\n", r, x); 1277 | break; 1278 | } 1279 | y ^= ak; 1280 | } 1281 | } 1282 | /* should not happen but check anyway */ 1283 | return remaining ? -1 : 0; 1284 | } 1285 | 1286 | static void *bch_alloc(size_t size, int *err) 1287 | { 1288 | void *ptr; 1289 | 1290 | ptr = kmalloc(size, GFP_KERNEL); 1291 | if (ptr == NULL) 1292 | *err = 1; 1293 | return ptr; 1294 | } 1295 | 1296 | /* 1297 | * compute generator polynomial for given (m,t) parameters. 1298 | */ 1299 | static uint32_t *compute_generator_polynomial(struct bch_control *bch) 1300 | { 1301 | const unsigned int m = GF_M(bch); 1302 | const unsigned int t = GF_T(bch); 1303 | int n, err = 0; 1304 | unsigned int i, j, nbits, r, word, *roots; 1305 | struct gf_poly *g; 1306 | uint32_t *genpoly; 1307 | 1308 | g = bch_alloc(GF_POLY_SZ(m*t), &err); 1309 | roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); 1310 | genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); 1311 | 1312 | if (err) { 1313 | kfree(genpoly); 1314 | genpoly = NULL; 1315 | goto finish; 1316 | } 1317 | 1318 | /* enumerate all roots of g(X) */ 1319 | memset(roots , 0, (bch->n+1)*sizeof(*roots)); 1320 | for (i = 0; i < t; i++) { 1321 | for (j = 0, r = 2*i+1; j < m; j++) { 1322 | roots[r] = 1; 1323 | r = mod_s(bch, 2*r); 1324 | } 1325 | } 1326 | /* build generator polynomial g(X) */ 1327 | g->deg = 0; 1328 | g->c[0] = 1; 1329 | for (i = 0; i < GF_N(bch); i++) { 1330 | if (roots[i]) { 1331 | /* multiply g(X) by (X+root) */ 1332 | r = bch->a_pow_tab[i]; 1333 | g->c[g->deg+1] = 1; 1334 | for (j = g->deg; j > 0; j--) 1335 | g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; 1336 | 1337 | g->c[0] = gf_mul(bch, g->c[0], r); 1338 | g->deg++; 1339 | } 1340 | } 1341 | /* store left-justified binary representation of g(X) */ 1342 | n = g->deg+1; 1343 | i = 0; 1344 | 1345 | while (n > 0) { 1346 | nbits = (n > 32) ? 32 : n; 1347 | for (j = 0, word = 0; j < nbits; j++) { 1348 | if (g->c[n-1-j]) 1349 | word |= 1u << (31-j); 1350 | } 1351 | genpoly[i++] = word; 1352 | n -= nbits; 1353 | } 1354 | bch->ecc_bits = g->deg; 1355 | 1356 | finish: 1357 | kfree(g); 1358 | kfree(roots); 1359 | 1360 | return genpoly; 1361 | } 1362 | 1363 | /** 1364 | * bch_init - initialize a BCH encoder/decoder 1365 | * @m: Galois field order, should be in the range 5-31 1366 | * @t: maximum error correction capability, in bits 1367 | * @prim_poly: user-provided primitive polynomial (or 0 to use default) 1368 | * @swap_bits: swap bits within data and syndrome bytes 1369 | * 1370 | * Returns: 1371 | * a newly allocated BCH control structure if successful, NULL otherwise 1372 | * 1373 | * This initialization can take some time, as lookup tables are built for fast 1374 | * encoding/decoding; make sure not to call this function from a time critical 1375 | * path. Usually, bch_init() should be called on module/driver init and 1376 | * bch_free() should be called to release memory on exit. 1377 | * 1378 | * You may provide your own primitive polynomial of degree @m in argument 1379 | * @prim_poly, or let bch_init() use its default polynomial. 1380 | * 1381 | * Once bch_init() has successfully returned a pointer to a newly allocated 1382 | * BCH control structure, ecc length in bytes is given by member @ecc_bytes of 1383 | * the structure. 1384 | */ 1385 | struct bch_control *bch_init(int m, int t, unsigned int prim_poly, 1386 | bool swap_bits) 1387 | { 1388 | int err = 0; 1389 | unsigned int i, words; 1390 | uint32_t *genpoly; 1391 | struct bch_control *bch = NULL; 1392 | 1393 | const int min_m = 5; 1394 | 1395 | /* default primitive polynomials */ 1396 | static const unsigned int prim_poly_tab[] = { 1397 | 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, 1398 | 0x402b, 0x8003, 1399 | }; 1400 | 1401 | #if defined(CONFIG_BCH_CONST_PARAMS) 1402 | if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { 1403 | printk(KERN_ERR "bch encoder/decoder was configured to support " 1404 | "parameters m=%d, t=%d only!\n", 1405 | CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); 1406 | goto fail; 1407 | } 1408 | #endif 1409 | if ((m < min_m) || (m > BCH_MAX_M)) 1410 | /* 1411 | * values of m greater than 31 are not currently supported; 1412 | * supporting m > 31 would require changing table base type 1413 | * (uint32_t) and a small patch in matrix transposition 1414 | */ 1415 | goto fail; 1416 | 1417 | if (t > BCH_MAX_T) 1418 | /* 1419 | * we can support larger than 64 bits if necessary, at the 1420 | * cost of higher stack usage. 1421 | */ 1422 | goto fail; 1423 | 1424 | /* sanity checks */ 1425 | if ((t < 1) || (m*t >= ((1 << m)-1))) 1426 | /* invalid t value */ 1427 | goto fail; 1428 | 1429 | /* select a primitive polynomial for generating GF(2^m) */ 1430 | if (prim_poly == 0) 1431 | prim_poly = prim_poly_tab[m-min_m]; 1432 | 1433 | bch = kzalloc(sizeof(*bch), GFP_KERNEL); 1434 | if (bch == NULL) 1435 | goto fail; 1436 | 1437 | bch->m = m; 1438 | bch->t = t; 1439 | bch->n = (1 << m)-1; 1440 | words = DIV_ROUND_UP(m*t, 32); 1441 | bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); 1442 | bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); 1443 | bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); 1444 | bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); 1445 | bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); 1446 | bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); 1447 | bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); 1448 | bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); 1449 | bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); 1450 | bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); 1451 | bch->swap_bits = swap_bits; 1452 | bch->prim_poly = prim_poly; 1453 | 1454 | for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) 1455 | bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); 1456 | 1457 | if (err) 1458 | goto fail; 1459 | 1460 | err = build_gf_tables(bch, prim_poly); 1461 | if (err) 1462 | goto fail; 1463 | 1464 | /* use generator polynomial for computing encoding tables */ 1465 | genpoly = compute_generator_polynomial(bch); 1466 | if (genpoly == NULL) 1467 | goto fail; 1468 | 1469 | build_mod8_tables(bch, genpoly); 1470 | kfree(genpoly); 1471 | 1472 | err = build_deg2_base(bch); 1473 | if (err) 1474 | goto fail; 1475 | 1476 | return bch; 1477 | 1478 | fail: 1479 | bch_free(bch); 1480 | return NULL; 1481 | } 1482 | EXPORT_SYMBOL_GPL(bch_init); 1483 | 1484 | /** 1485 | * bch_free - free the BCH control structure 1486 | * @bch: BCH control structure to release 1487 | */ 1488 | void bch_free(struct bch_control *bch) 1489 | { 1490 | unsigned int i; 1491 | 1492 | if (bch) { 1493 | kfree(bch->a_pow_tab); 1494 | kfree(bch->a_log_tab); 1495 | kfree(bch->mod8_tab); 1496 | kfree(bch->ecc_buf); 1497 | kfree(bch->ecc_buf2); 1498 | kfree(bch->xi_tab); 1499 | kfree(bch->syn); 1500 | kfree(bch->cache); 1501 | kfree(bch->elp); 1502 | 1503 | for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) 1504 | kfree(bch->poly_2t[i]); 1505 | 1506 | kfree(bch); 1507 | } 1508 | } 1509 | EXPORT_SYMBOL_GPL(bch_free); 1510 | 1511 | MODULE_LICENSE("GPL"); 1512 | MODULE_AUTHOR("Ivan Djelic "); 1513 | MODULE_DESCRIPTION("Binary BCH encoder/decoder"); 1514 | -------------------------------------------------------------------------------- /src/bch.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | * Generic binary BCH encoding/decoding library 4 | * 5 | * Copyright © 2011 Parrot S.A. 6 | * 7 | * Author: Ivan Djelic 8 | * 9 | * Description: 10 | * 11 | * This library provides runtime configurable encoding/decoding of binary 12 | * Bose-Chaudhuri-Hocquenghem (BCH) codes. 13 | */ 14 | #ifndef _BCH_H 15 | #define _BCH_H 16 | 17 | #if defined(__KERNEL__) 18 | #include 19 | #else 20 | #include 21 | #include 22 | #endif 23 | 24 | /** 25 | * struct bch_control - BCH control structure 26 | * @m: Galois field order 27 | * @n: maximum codeword size in bits (= 2^m-1) 28 | * @t: error correction capability in bits 29 | * @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) 30 | * @ecc_bytes: ecc max size (m*t bits) in bytes 31 | * @a_pow_tab: Galois field GF(2^m) exponentiation lookup table 32 | * @a_log_tab: Galois field GF(2^m) log lookup table 33 | * @mod8_tab: remainder generator polynomial lookup tables 34 | * @ecc_buf: ecc parity words buffer 35 | * @ecc_buf2: ecc parity words buffer 36 | * @xi_tab: GF(2^m) base for solving degree 2 polynomial roots 37 | * @syn: syndrome buffer 38 | * @cache: log-based polynomial representation buffer 39 | * @elp: error locator polynomial 40 | * @poly_2t: temporary polynomials of degree 2t 41 | * @swap_bits: swap bits within data and syndrome bytes 42 | */ 43 | struct bch_control { 44 | unsigned int m; 45 | unsigned int n; 46 | unsigned int t; 47 | unsigned int ecc_bits; 48 | unsigned int ecc_bytes; 49 | unsigned int prim_poly; 50 | /* private: */ 51 | uint32_t *a_pow_tab; 52 | uint32_t *a_log_tab; 53 | uint32_t *mod8_tab; 54 | uint32_t *ecc_buf; 55 | uint32_t *ecc_buf2; 56 | unsigned int *xi_tab; 57 | unsigned int *syn; 58 | int *cache; 59 | struct gf_poly *elp; 60 | struct gf_poly *poly_2t[4]; 61 | bool swap_bits; 62 | }; 63 | 64 | struct bch_control *bch_init(int m, int t, unsigned int prim_poly, 65 | bool swap_bits); 66 | 67 | void bch_free(struct bch_control *bch); 68 | 69 | void bch_encode(struct bch_control *bch, const uint8_t *data, 70 | unsigned int len, uint8_t *ecc); 71 | 72 | void bch_compute_even_syndromes(struct bch_control *bch, unsigned int *syn); 73 | 74 | int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len, 75 | const uint8_t *recv_ecc, const uint8_t *calc_ecc, 76 | const unsigned int *syn, unsigned int *errloc); 77 | 78 | #endif /* _BCH_H */ 79 | -------------------------------------------------------------------------------- /src/bchlib.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* Python C module for BCH encoding/decoding/correcting. */ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "bch.h" 12 | 13 | 14 | #if (defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) 15 | # define alloca(size) _alloca(size) 16 | # include 17 | #else 18 | # include 19 | #endif 20 | 21 | 22 | typedef struct { 23 | PyObject_HEAD 24 | struct bch_control *bch; 25 | unsigned int *errloc; 26 | int nerr; 27 | } BCHObject; 28 | 29 | static void 30 | BCH_dealloc(BCHObject *self) 31 | { 32 | if (self->bch) { 33 | bch_free(self->bch); 34 | self->bch = NULL; 35 | } 36 | 37 | if (self->errloc) { 38 | free(self->errloc); 39 | self->errloc = NULL; 40 | } 41 | 42 | Py_TYPE(self)->tp_free((PyObject *)self); 43 | } 44 | 45 | static int 46 | BCH_init(BCHObject *self, PyObject *args, PyObject *kwds) 47 | { 48 | int t, m = -1; 49 | unsigned int prim_poly = 0; 50 | int swap_bits = false; 51 | 52 | static char *kwlist[] = {"t", "prim_poly", "m", "swap_bits", NULL}; 53 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|Iip", kwlist, &t, 54 | &prim_poly, &m, &swap_bits)) { 55 | return -1; 56 | } 57 | 58 | if (m == -1 && prim_poly == 0) { 59 | PyErr_SetString(PyExc_ValueError, 60 | "'m' and/or 'poly' must be provided"); 61 | return -1; 62 | } 63 | 64 | if (m == -1) { 65 | unsigned int tmp = prim_poly; 66 | m = 0; 67 | while (tmp >>= 1) { 68 | m++; 69 | } 70 | } 71 | 72 | self->bch = bch_init(m, t, prim_poly, swap_bits); 73 | if (!self->bch) { 74 | PyErr_SetString(PyExc_RuntimeError, 75 | "unable to inititalize bch, invalid parameters?"); 76 | return -1; 77 | } 78 | 79 | self->errloc = calloc(1, sizeof(unsigned int) * self->bch->t); 80 | if (!self->errloc) { 81 | bch_free(self->bch); 82 | self->bch = NULL; 83 | PyErr_SetString(PyExc_MemoryError, "unable to allocate errloc buffer"); 84 | return -1; 85 | } 86 | 87 | memset(self->bch->syn, 0, sizeof(unsigned int) * 2*self->bch->t); 88 | 89 | return 0; 90 | } 91 | 92 | static PyObject * 93 | BCH_encode(BCHObject *self, PyObject *args, PyObject *kwds) 94 | { 95 | Py_buffer data = {0}; 96 | Py_buffer ecc = {0}; 97 | 98 | static char *kwlist[] = {"data", "ecc", NULL}; 99 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "y*|y*", kwlist, &data, 100 | &ecc)) { 101 | return NULL; 102 | } 103 | 104 | if (!ecc.buf) { 105 | ecc.len = self->bch->ecc_bytes; 106 | ecc.buf = alloca(ecc.len); 107 | memset(ecc.buf, 0, ecc.len); 108 | } else if (ecc.len != self->bch->ecc_bytes) { 109 | PyErr_Format(PyExc_ValueError, "ecc length must be %d bytes", 110 | self->bch->ecc_bytes); 111 | return NULL; 112 | } 113 | 114 | bch_encode(self->bch, (uint8_t *) data.buf, (unsigned int) data.len, 115 | ecc.buf); 116 | 117 | return PyBytes_FromStringAndSize((const char *) ecc.buf, 118 | self->bch->ecc_bytes); 119 | } 120 | 121 | static PyObject * 122 | BCH_decode(BCHObject *self, PyObject *args, PyObject *kwds) 123 | { 124 | Py_buffer data = {0}; 125 | Py_buffer recv_ecc = {0}; 126 | Py_buffer calc_ecc = {0}; 127 | PyObject *syn = NULL; 128 | 129 | static char *kwlist[] = {"data", "recv_ecc", "calc_ecc", "syn", NULL}; 130 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "|y*y*y*O", kwlist, &data, 131 | &recv_ecc, &calc_ecc, &syn)) { 132 | return NULL; 133 | } 134 | 135 | if (recv_ecc.buf && recv_ecc.len != self->bch->ecc_bytes) { 136 | PyErr_Format(PyExc_ValueError, "recv_ecc length should be %d bytes", 137 | self->bch->ecc_bytes); 138 | return NULL; 139 | } 140 | 141 | if (calc_ecc.buf && calc_ecc.len != self->bch->ecc_bytes) { 142 | PyErr_Format(PyExc_ValueError, "calc_ecc length should be %d bytes", 143 | self->bch->ecc_bytes); 144 | return NULL; 145 | } 146 | 147 | if (syn) { 148 | Py_INCREF(syn); 149 | 150 | if (!PySequence_Check(syn)) { 151 | PyErr_SetString(PyExc_TypeError, "'syn' must be a sequence type"); 152 | Py_DECREF(syn); 153 | return NULL; 154 | } 155 | 156 | if (PySequence_Length(syn) != 2*self->bch->t) { 157 | PyErr_Format(PyExc_ValueError, "'syn' must have %d elements", 158 | 2*self->bch->t); 159 | Py_DECREF(syn); 160 | return NULL; 161 | } 162 | 163 | for (unsigned int i = 0; i < 2*self->bch->t; i++) { 164 | PyObject *value = PySequence_GetItem(syn, i); 165 | Py_INCREF(value); 166 | long ltmp = PyLong_AsLong(value); 167 | if (ltmp == -1 && PyErr_Occurred()) { 168 | Py_DECREF(value); 169 | Py_DECREF(syn); 170 | return NULL; 171 | } 172 | self->bch->syn[i] = ltmp; 173 | Py_DECREF(value); 174 | } 175 | 176 | Py_DECREF(syn); 177 | } 178 | 179 | self->nerr = bch_decode(self->bch, data.buf, data.len, recv_ecc.buf, 180 | calc_ecc.buf, syn ? self->bch->syn : NULL, self->errloc); 181 | 182 | if (self->nerr < 0) { 183 | if (self->nerr == -EINVAL) { 184 | PyErr_SetString(PyExc_ValueError, "invalid parameters"); 185 | return NULL; 186 | } else if (self->nerr == -EBADMSG) { 187 | self->nerr = -1; 188 | } else { 189 | return NULL; 190 | } 191 | } 192 | 193 | return PyLong_FromLong(self->nerr); 194 | } 195 | 196 | static PyObject * 197 | BCH_correct(BCHObject *self, PyObject *args, PyObject *kwds) 198 | { 199 | Py_buffer data = {0}; 200 | Py_buffer ecc = {0}; 201 | PyObject *result = NULL; 202 | 203 | static char *kwlist[] = {"data", "ecc", NULL}; 204 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "y*|y*", kwlist, &data, 205 | &ecc)) { 206 | goto cleanup; 207 | } 208 | 209 | if (data.readonly) { 210 | PyErr_SetString(PyExc_ValueError, "data cannot be readonly"); 211 | goto cleanup; 212 | } 213 | 214 | if (ecc.readonly) { 215 | PyErr_SetString(PyExc_ValueError, "ecc cannot be readonly"); 216 | goto cleanup; 217 | } 218 | 219 | if (self->nerr < 0) { 220 | goto done; 221 | } 222 | 223 | for (int i = 0; i < self->nerr; i++) { 224 | unsigned int bitnum = self->errloc[i]; 225 | if (bitnum >= (data.len + self->bch->ecc_bytes) * 8) { 226 | PyErr_SetString(PyExc_IndexError, "uncorrectable error"); 227 | return NULL; 228 | } 229 | unsigned int byte = bitnum / 8; 230 | unsigned char bit = 1 << (bitnum & 7); 231 | 232 | if (byte < data.len) { 233 | if (data.buf && !data.readonly) { 234 | ((uint8_t *) data.buf)[byte] ^= bit; 235 | } 236 | } else { 237 | if (ecc.buf && !ecc.readonly) { 238 | ((uint8_t *) ecc.buf)[byte - data.len] ^= bit; 239 | } 240 | } 241 | } 242 | 243 | done: 244 | result = Py_None; 245 | Py_IncRef(Py_None); 246 | 247 | cleanup: 248 | PyBuffer_Release(&data); 249 | PyBuffer_Release(&ecc); 250 | return result; 251 | } 252 | 253 | static PyObject * 254 | BCH_compute_even_syn(BCHObject *self, PyObject *args, PyObject *kwds) 255 | { 256 | PyObject *syn; 257 | PyObject *result = NULL; 258 | unsigned int *result_syn = alloca(sizeof(unsigned int) * 2 * self->bch->t); 259 | 260 | static char *kwlist[] = {"syn", NULL}; 261 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &syn)) { 262 | return result; 263 | } 264 | 265 | if (!PySequence_Check(syn)) { 266 | PyErr_SetString(PyExc_TypeError, "'syn' must be a sequence type"); 267 | return result; 268 | } 269 | 270 | if (PySequence_Length(syn) != 2*self->bch->t) { 271 | PyErr_Format(PyExc_ValueError, "'syn' must have %d elements", 272 | 2*self->bch->t); 273 | return result; 274 | } 275 | 276 | for (unsigned int i = 0; i < 2*self->bch->t; i++) { 277 | PyObject *value = PySequence_GetItem(syn, i); 278 | long tmp = PyLong_AsLong(value); 279 | if (tmp == -1 && PyErr_Occurred()) { 280 | return result; 281 | } 282 | result_syn[i] = tmp; 283 | } 284 | 285 | bch_compute_even_syndromes(self->bch, result_syn); 286 | 287 | result = PyTuple_New(2*self->bch->t); 288 | for (unsigned int i = 0; i < 2*self->bch->t; i++) { 289 | PyObject *value = PyLong_FromLong(result_syn[i]); 290 | PyTuple_SetItem(result, i, value); 291 | } 292 | 293 | return result; 294 | } 295 | 296 | static PyObject * 297 | BCH_getattr(BCHObject *self, PyObject *name) 298 | { 299 | PyObject *value; 300 | PyObject *result = NULL; 301 | 302 | if (!PyUnicode_Check(name)) { 303 | PyErr_Format(PyExc_TypeError, 304 | "attribute name must be a string, not '%.200s'", 305 | Py_TYPE(name)->tp_name); 306 | return NULL; 307 | } 308 | const char *cname = PyUnicode_AsUTF8(name); 309 | 310 | if (strcmp(cname, "ecc_bits") == 0) { 311 | result = PyLong_FromLong(self->bch->ecc_bits); 312 | } else if (strcmp(cname, "ecc_bytes") == 0) { 313 | result = PyLong_FromLong(self->bch->ecc_bytes); 314 | } else if (strcmp(cname, "errloc") == 0) { 315 | result = PyTuple_New(self->nerr <= 0 ? 0 : self->nerr); 316 | for (int i = 0; i < self->nerr; i++) { 317 | value = PyLong_FromLong(self->errloc[i]); 318 | PyTuple_SetItem(result, i, value); 319 | } 320 | } else if (strcmp(cname, "m") == 0) { 321 | result = PyLong_FromLong(self->bch->m); 322 | } else if (strcmp(cname, "n") == 0) { 323 | result = PyLong_FromLong(self->bch->n); 324 | } else if (strcmp(cname, "prim_poly") == 0) { 325 | result = PyLong_FromLong(self->bch->prim_poly); 326 | } else if (strcmp(cname, "syn") == 0) { 327 | if (self->bch->syn) { 328 | result = PyTuple_New(2*self->bch->t); 329 | for (unsigned int i = 0; i < 2*self->bch->t; i++) { 330 | value = PyLong_FromLong(self->bch->syn[i]); 331 | PyTuple_SetItem(result, i, value); 332 | } 333 | } 334 | else { 335 | result = Py_None; 336 | Py_INCREF(result); 337 | } 338 | } else if (strcmp(cname, "t") == 0) { 339 | result = PyLong_FromLong(self->bch->t); 340 | } else { 341 | result = PyObject_GenericGetAttr((PyObject *)self, name); 342 | } 343 | 344 | return result; 345 | } 346 | 347 | static PyMemberDef BCH_members[] = { 348 | {"ecc_bits", -1, 0, READONLY|RESTRICTED, 349 | "Readonly; number of ecc bits."}, 350 | {"ecc_bytes", -1, 0, READONLY|RESTRICTED, 351 | "Readonly; number of ecc bytes."}, 352 | {"errloc", -1, 0, READONLY|RESTRICTED, 353 | "Readonly; tuple of error bit locations."}, 354 | {"m", -1, 0, READONLY|RESTRICTED, 355 | "Readonly; Galois field order."}, 356 | {"n", -1, 0, READONLY|RESTRICTED, 357 | "Readonly; maximum codeword size in bits."}, 358 | {"prim_poly", -1, 0, READONLY|RESTRICTED, 359 | "Readonly; primitive polynomial for bch operations."}, 360 | {"syn", -1, 0, READONLY|RESTRICTED, 361 | "Readonly; a tuple of syndromes after performing a decode()."}, 362 | {"t", -1, 0, READONLY|RESTRICTED, 363 | "Readonly; the number of bit errors that can be corrected."}, 364 | {NULL} 365 | }; 366 | 367 | static PyMethodDef BCH_methods[] = { 368 | {"encode", (PyCFunction) BCH_encode, METH_VARARGS | METH_KEYWORDS, "\b\b\b\b" 369 | "encode(data[, ecc]) → ecc\n" 370 | "Encodes 'data' with an optional starting 'ecc'. Returns the\n" 371 | "calculated ecc."}, 372 | {"decode", (PyCFunction) BCH_decode, METH_VARARGS | METH_KEYWORDS, "\b\b\b\b" 373 | "decode(data=None, recv_ecc=None, calc_ecc=None, syn=None) → nerr\n" 374 | "Calculates error locations and returns the number of errors found\n" 375 | "or negative if decoding failed.\n\n" 376 | 377 | "There are four ways that 'decode' can function by providing\n" 378 | "different input parameters:\n\n" 379 | 380 | " 'data' and 'recv_ecc'\n" 381 | " 'recv_ecc' and 'calc_ecc'\n" 382 | " 'calc_ecc' (as recv_ecc XOR calc_ecc)\n" 383 | " 'syn' (a sequence of 2*t values)"}, 384 | {"correct", (PyCFunction) BCH_correct, METH_VARARGS | METH_KEYWORDS, "\b\b\b\b" 385 | "correct(data=None, ecc=None) → None\n" 386 | "Corrects 'data' and 'ecc' if provided. Buffers must not be\n" 387 | "readonly."}, 388 | {"compute_even_syn", (PyCFunction) BCH_compute_even_syn, 389 | METH_VARARGS | METH_KEYWORDS, "\b\b\b\b" 390 | "compute_even_syn(syn) → syn\n" 391 | "Computes even syndromes from odd ones. Takes a sequence of\n" 392 | "2*t values and returns a tuple of 2*t elements."}, 393 | {NULL} 394 | }; 395 | 396 | static PyTypeObject BCHType = { 397 | PyVarObject_HEAD_INIT(NULL, 0) 398 | .tp_name = "bchlib.BCH", 399 | .tp_basicsize = sizeof(BCHObject), 400 | .tp_dealloc = (destructor) BCH_dealloc, 401 | .tp_getattro = (getattrofunc) BCH_getattr, 402 | .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 403 | .tp_doc = 404 | "BCH Encoder/Decoder\n\n" 405 | 406 | "__init__(t, poly=None, m=None, swap_bits=False) → bch\n" 407 | " Constructor creates a BCH object with given 't' bit strength. At\n" 408 | " least one of 'poly' and/or 'm' must be provided. If 'poly' is\n" 409 | " provided but 'm' (Galois field order) is not, 'm' will be\n" 410 | " calculated automatically. If 'm' between 5 and 15 inclusive is'\n" 411 | " provided, 'polywill be selected automatically. The 'swap_bits'\n" 412 | " parameter will reverse the bit order within data and syndrome\n" 413 | " bytes.", 414 | .tp_methods = BCH_methods, 415 | .tp_members = BCH_members, 416 | .tp_init = (initproc) BCH_init, 417 | .tp_new = PyType_GenericNew, 418 | }; 419 | 420 | static PyMethodDef module_methods[] = { 421 | {NULL} 422 | }; 423 | 424 | static struct PyModuleDef moduledef = { 425 | PyModuleDef_HEAD_INIT, 426 | "BCH", 427 | "BCH Library", 428 | -1, 429 | module_methods, 430 | NULL, 431 | NULL, 432 | NULL, 433 | NULL, 434 | }; 435 | 436 | PyMODINIT_FUNC 437 | PyInit_bchlib(void) 438 | { 439 | PyObject *m; 440 | 441 | if (PyType_Ready(&BCHType) < 0) 442 | return NULL; 443 | m = PyModule_Create(&moduledef); 444 | if (m == NULL) 445 | return NULL; 446 | 447 | Py_INCREF(&BCHType); 448 | PyModule_AddObject(m, "BCH", (PyObject *)&BCHType); 449 | 450 | return m; 451 | } 452 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkent/python-bchlib/8d0656ab8f37e734428635501738d360ad80eebd/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_chaining.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import bchlib 4 | import unittest 5 | 6 | class Tests(unittest.TestCase): 7 | def test(self): 8 | bch = bchlib.BCH(16, m=13) 9 | ecc = bch.encode(b'First') 10 | ecc = bch.encode(b'Second', ecc=ecc) 11 | assert(bch.decode(b'FirstSecond', ecc) == 0) 12 | 13 | if __name__ == '__main__': 14 | unittest.main() 15 | -------------------------------------------------------------------------------- /tests/test_consecutive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import bchlib 4 | import unittest 5 | 6 | class Tests(unittest.TestCase): 7 | def test(self): 8 | bch = bchlib.BCH(16, m=13) 9 | ecc1 = bch.encode(b'First') 10 | ecc2 = bch.encode(b'Second') 11 | assert(bch.decode(b'First', ecc1) == 0) 12 | assert(bch.decode(b'Second', ecc2) == 0) 13 | 14 | if __name__ == '__main__': 15 | unittest.main() 16 | -------------------------------------------------------------------------------- /tests/test_exercise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import random 5 | import unittest 6 | 7 | import bchlib 8 | 9 | 10 | def bitflip(data): 11 | bit = random.randint(0, len(data) * 8 - 1) 12 | data[bit // 8] ^= 1 << (bit % 8) 13 | 14 | class Tests(unittest.TestCase): 15 | def exercise(self, *args, **kwargs): 16 | bch = bchlib.BCH(*args, **kwargs) 17 | bits = bch.n - bch.ecc_bits 18 | 19 | # print(f'data:\t{bits} bits (usable bytes: {bits // 8})') 20 | # print(f'ecc: \t{bch.ecc_bits} bits (bytes: {bch.ecc_bytes})') 21 | # print(f'm: \t{bch.m}') 22 | # print(f'n: \t{bch.n} ({(bch.n + 7) // 8} bytes)') 23 | # print(f'poly:\t{bch.prim_poly}') 24 | # print(f't: \t{bch.t}') 25 | 26 | data = bytearray(os.urandom(bits // 8)) 27 | ecc = bch.encode(data) 28 | packet = data + ecc 29 | corrupted_packet = bytearray(packet) 30 | 31 | for _ in range(bch.t): 32 | bitflip(corrupted_packet) 33 | 34 | corrupted_data = corrupted_packet[:-bch.ecc_bytes] 35 | corrupted_ecc = corrupted_packet[-bch.ecc_bytes:] 36 | 37 | nerr = bch.decode(corrupted_data, corrupted_ecc) 38 | # print(f'nerr: \t{nerr}') 39 | assert(nerr >= 0) 40 | # print(bch.errloc) 41 | 42 | corrected_data = bytearray(corrupted_data) 43 | corrected_ecc = bytearray(corrupted_ecc) 44 | bch.correct(corrected_data, corrected_ecc) 45 | corrected_packet = corrected_data + corrected_ecc 46 | 47 | assert(corrected_packet == packet) 48 | 49 | def test_t_eq_6_285(self): 50 | for _ in range(1000): 51 | self.exercise(6, prim_poly=285) 52 | 53 | def test_t_eq_6_285_swap(self): 54 | for _ in range(1000): 55 | self.exercise(6, prim_poly=285, swap_bits=True) 56 | 57 | def test_t_eq_6_487(self): 58 | for _ in range(1000): 59 | self.exercise(6, prim_poly=487) 60 | 61 | def test_t_eq_6_487_swap(self): 62 | for _ in range(1000): 63 | self.exercise(6, prim_poly=487, swap_bits=True) 64 | 65 | def test_t_eq_12_17475(self): 66 | for _ in range(1000): 67 | self.exercise(12, prim_poly=17475) 68 | 69 | def test_t_eq_12_17475_swap(self): 70 | for _ in range(1000): 71 | self.exercise(12, prim_poly=17475, swap_bits=True) 72 | 73 | def test_t_eq_16(self): 74 | for _ in range(1000): 75 | self.exercise(16, m=13) 76 | 77 | def test_t_eq_16_swap(self): 78 | for _ in range(1000): 79 | self.exercise(16, m=13, swap_bits=True) 80 | 81 | def test_t_eq_32(self): 82 | for _ in range(1000): 83 | self.exercise(32, m=14) 84 | 85 | def test_t_eq_32_swap(self): 86 | for _ in range(1000): 87 | self.exercise(32, m=14, swap_bits=True) 88 | 89 | def test_t_eq_64(self): 90 | for _ in range(1000): 91 | self.exercise(64, m=15) 92 | 93 | def test_t_eq_64_swap(self): 94 | for _ in range(1000): 95 | self.exercise(64, m=15, swap_bits=True) 96 | 97 | if __name__ == '__main__': 98 | unittest.main() 99 | -------------------------------------------------------------------------------- /tests/test_exynos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """16-bit ECC encoder as in the Samsung S5PV210 and Exynos NAND controllers""" 4 | 5 | import bchlib 6 | import unittest 7 | 8 | class Tests(unittest.TestCase): 9 | def test(self): 10 | ECC_POLY = 8219 11 | ECC_BITS = 16 12 | 13 | xor_data = bytearray(b'\x9A\xD7\xEF\x91\x88\x80\xFB\xF7' \ 14 | b'\x06\x3A\x5C\x9F\x49\x24\xD0\x75' \ 15 | b'\x02\xE3\x59\xE0\xE4\xBC\x1E\x20' \ 16 | b'\x70\x2E') 17 | 18 | def xor_ecc(ecc): 19 | new_ecc = bytearray() 20 | for a, b in zip(ecc, xor_data): 21 | new_ecc.append(a ^ b) 22 | return new_ecc 23 | 24 | bch = bchlib.BCH(ECC_BITS, prim_poly=ECC_POLY) 25 | ecc = bch.encode(b'\xFF' * 512) 26 | ecc = xor_ecc(ecc) 27 | 28 | assert ecc == b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' \ 29 | b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' \ 30 | b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' \ 31 | b'\xFF\xFF' 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | --------------------------------------------------------------------------------