├── .gitignore ├── LICENSE.GPLv2 ├── LICENSE.zlib ├── Makefile ├── README.md ├── crc32.c ├── fwupgrade-cgi.c ├── fwupgrade-cgi.h ├── fwupgrade-doc.txt ├── fwupgrade-file.c ├── fwupgrade-file.h ├── fwupgrade-tool.c ├── fwupgrade-ubi-example.conf ├── fwupgrade-uboot-env.c ├── fwupgrade-uboot-env.h ├── fwupgrade.c ├── fwupgrade.conf ├── fwupgrade.h ├── index.html └── md5.c /.gitignore: -------------------------------------------------------------------------------- 1 | fwupgrade 2 | fwupgrade-tool 3 | -------------------------------------------------------------------------------- /LICENSE.GPLv2: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, 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 Library 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) 19yy 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 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /LICENSE.zlib: -------------------------------------------------------------------------------- 1 | Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | Jean-loup Gailly Mark Adler 20 | jloup@gzip.org madler@alumni.caltech.edu 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS=-Wall 3 | HOSTCC?=gcc 4 | 5 | all: fwupgrade fwupgrade-tool 6 | 7 | fwupgrade: fwupgrade.c fwupgrade-cgi.c fwupgrade-file.c fwupgrade-uboot-env.c md5.c crc32.c 8 | $(CC) -o $@ $^ $(CFLAGS) 9 | 10 | fwupgrade-tool: fwupgrade-tool.c md5.c 11 | $(HOSTCC) -o $@ $^ $(CFLAGS) 12 | 13 | clean: 14 | $(RM) *.o fwupgrade-tool fwupgrade 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fwupgrade 2 | 3 | Do not use this tool for new projects! Please use 4 | [swupdate](https://sbabic.github.io/swupdate/), 5 | [Mender](https://mender.io/) or [RAUC](https://www.rauc.io/). 6 | 7 | *fwupgrade* was written many years ago, back when the above solutions 8 | didn't exist. *fwupgrade* is not maintained, poorly written, has no 9 | security features and probably lots of other issues. You have been 10 | warned. 11 | 12 | # License 13 | 14 | *fwupgrade* is published under the terms of the GPLv2 license, which 15 | text is available in LICENSE.GPLv2. 16 | 17 | *fwupgrade* includes code from the U-Boot project (md5.c, 18 | crc32.c). crc32.c itself comes from the Zlib project, and is released 19 | under the Zlib license, which text is available in LICENSE.zlib. 20 | -------------------------------------------------------------------------------- /crc32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is derived from crc32.c from the zlib-1.1.3 distribution 3 | * by Jean-loup Gailly and Mark Adler. 4 | */ 5 | 6 | /* crc32.c -- compute the CRC-32 of a data stream 7 | * Copyright (C) 1995-1998 Mark Adler 8 | * For conditions of distribution and use, see copyright notice in zlib.h 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #if __BYTE_ORDER == __LITTLE_ENDIAN 16 | #define tole(x) (x) 17 | #else 18 | #define tole(x) __bswap_constant_32(x) 19 | #endif 20 | 21 | #ifdef DYNAMIC_CRC_TABLE 22 | 23 | static int crc_table_empty = 1; 24 | static uint32_t crc_table[256]; 25 | static void make_crc_table OF((void)); 26 | 27 | /* 28 | Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: 29 | x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. 30 | 31 | Polynomials over GF(2) are represented in binary, one bit per coefficient, 32 | with the lowest powers in the most significant bit. Then adding polynomials 33 | is just exclusive-or, and multiplying a polynomial by x is a right shift by 34 | one. If we call the above polynomial p, and represent a byte as the 35 | polynomial q, also with the lowest power in the most significant bit (so the 36 | byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, 37 | where a mod b means the remainder after dividing a by b. 38 | 39 | This calculation is done using the shift-register method of multiplying and 40 | taking the remainder. The register is initialized to zero, and for each 41 | incoming bit, x^32 is added mod p to the register if the bit is a one (where 42 | x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by 43 | x (which is shifting right by one and adding x^32 mod p if the bit shifted 44 | out is a one). We start with the highest power (least significant bit) of 45 | q and repeat for all eight bits of q. 46 | 47 | The table is simply the CRC of all possible eight bit values. This is all 48 | the information needed to generate CRC's on data a byte at a time for all 49 | combinations of CRC register values and incoming bytes. 50 | */ 51 | static void make_crc_table() 52 | { 53 | uint32_t c; 54 | int n, k; 55 | uLong poly; /* polynomial exclusive-or pattern */ 56 | /* terms of polynomial defining this crc (except x^32): */ 57 | static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; 58 | 59 | /* make exclusive-or pattern from polynomial (0xedb88320L) */ 60 | poly = 0L; 61 | for (n = 0; n < sizeof(p)/sizeof(Byte); n++) 62 | poly |= 1L << (31 - p[n]); 63 | 64 | for (n = 0; n < 256; n++) 65 | { 66 | c = (uLong)n; 67 | for (k = 0; k < 8; k++) 68 | c = c & 1 ? poly ^ (c >> 1) : c >> 1; 69 | crc_table[n] = tole(c); 70 | } 71 | crc_table_empty = 0; 72 | } 73 | #else 74 | /* ======================================================================== 75 | * Table of CRC-32's of all single-byte values (made by make_crc_table) 76 | */ 77 | 78 | static const uint32_t crc_table[256] = { 79 | tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL), 80 | tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L), 81 | tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L), 82 | tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L), 83 | tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL), 84 | tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L), 85 | tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL), 86 | tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L), 87 | tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L), 88 | tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL), 89 | tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L), 90 | tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L), 91 | tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L), 92 | tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL), 93 | tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L), 94 | tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL), 95 | tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL), 96 | tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L), 97 | tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L), 98 | tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L), 99 | tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL), 100 | tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L), 101 | tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL), 102 | tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L), 103 | tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L), 104 | tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL), 105 | tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L), 106 | tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L), 107 | tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L), 108 | tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL), 109 | tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L), 110 | tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL), 111 | tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL), 112 | tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L), 113 | tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L), 114 | tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L), 115 | tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL), 116 | tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L), 117 | tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL), 118 | tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L), 119 | tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L), 120 | tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL), 121 | tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L), 122 | tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L), 123 | tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L), 124 | tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL), 125 | tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L), 126 | tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL), 127 | tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL), 128 | tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L), 129 | tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L), 130 | tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L), 131 | tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL), 132 | tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L), 133 | tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL), 134 | tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L), 135 | tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L), 136 | tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL), 137 | tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L), 138 | tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L), 139 | tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L), 140 | tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL), 141 | tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L), 142 | tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL) 143 | }; 144 | #endif 145 | 146 | #if 0 147 | /* ========================================================================= 148 | * This function can be used by asm versions of crc32() 149 | */ 150 | const uint32_t * get_crc_table() 151 | { 152 | #ifdef DYNAMIC_CRC_TABLE 153 | if (crc_table_empty) make_crc_table(); 154 | #endif 155 | return (const uint32_t *)crc_table; 156 | } 157 | #endif 158 | 159 | /* ========================================================================= */ 160 | # if __BYTE_ORDER == __LITTLE_ENDIAN 161 | # define DO_CRC(x) crc = tab[(crc ^ (x)) & 255] ^ (crc >> 8) 162 | # else 163 | # define DO_CRC(x) crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc << 8) 164 | # endif 165 | 166 | /* ========================================================================= */ 167 | 168 | /* No ones complement version. JFFS2 (and other things ?) 169 | * don't use ones compliment in their CRC calculations. 170 | */ 171 | uint32_t crc32_no_comp(uint32_t crc, const char *buf, unsigned int len) 172 | { 173 | const uint32_t *tab = crc_table; 174 | const uint32_t *b =(const uint32_t *)buf; 175 | size_t rem_len; 176 | #ifdef DYNAMIC_CRC_TABLE 177 | if (crc_table_empty) 178 | make_crc_table(); 179 | #endif 180 | crc = htole32(crc); 181 | /* Align it */ 182 | if (((long)b) & 3 && len) { 183 | uint8_t *p = (uint8_t *)b; 184 | do { 185 | DO_CRC(*p++); 186 | } while ((--len) && ((long)p)&3); 187 | b = (uint32_t *)p; 188 | } 189 | 190 | rem_len = len & 3; 191 | len = len >> 2; 192 | for (--b; len; --len) { 193 | /* load data 32 bits wide, xor data 32 bits wide. */ 194 | crc ^= *++b; /* use pre increment for speed */ 195 | DO_CRC(0); 196 | DO_CRC(0); 197 | DO_CRC(0); 198 | DO_CRC(0); 199 | } 200 | len = rem_len; 201 | /* And the last few bytes */ 202 | if (len) { 203 | uint8_t *p = (uint8_t *)(b + 1) - 1; 204 | do { 205 | DO_CRC(*++p); /* use pre increment for speed */ 206 | } while (--len); 207 | } 208 | 209 | return le32toh(crc); 210 | } 211 | #undef DO_CRC 212 | 213 | uint32_t crc32 (uint32_t crc, const char *p, unsigned int len) 214 | { 215 | return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL; 216 | } 217 | -------------------------------------------------------------------------------- /fwupgrade-cgi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fwupgrade-cgi.h" 7 | 8 | #define VALID_CONTENT_TYPE "multipart/form-data; boundary=" 9 | #define VALID_ELEMENT_CONTENT_DISPOSITION "Content-Disposition:" 10 | #define VALID_ELEMENT_CONTENT_TYPE "Content-Type: application/octet-stream" 11 | 12 | static char *nextline(char *s, unsigned int *remaining) 13 | { 14 | /* Go to the end of line */ 15 | while (*s != '\n' && *s != '\r' && *s != 0) { 16 | (*remaining)--; 17 | s++; 18 | } 19 | 20 | if (! *s) 21 | return NULL; 22 | 23 | if (*s == '\r') { 24 | (*remaining)--; 25 | s++; 26 | } 27 | else 28 | return NULL; 29 | 30 | if (*s == '\n') { 31 | (*remaining)--; 32 | s++; 33 | } 34 | else 35 | return NULL; 36 | 37 | return s; 38 | } 39 | 40 | static char *get_filename_from_content_disposition(char *buf) 41 | { 42 | char *p, *psave; 43 | char *localbuf, *tmp; 44 | int buflen; 45 | 46 | /* Eliminate the Content-Disposition header name */ 47 | buf += strlen(VALID_ELEMENT_CONTENT_DISPOSITION); 48 | 49 | /* Copy the header value so that it is 0 terminated, which 50 | * allows it to be parsed with strtok_r() */ 51 | buflen = strchr(buf, '\r') - buf; 52 | localbuf = malloc(buflen + 1); 53 | if (! localbuf) 54 | return NULL; 55 | memset(localbuf, 0, buflen + 1); 56 | strncpy(localbuf, buf, buflen); 57 | 58 | tmp = localbuf; 59 | 60 | /* This parses a string like 'form-data; name="file"; 61 | * filename="foobar.img"' and extracts 'foobar.img' */ 62 | while((p = strtok_r(tmp, ";", &psave)) != NULL) { 63 | char *delim; 64 | 65 | /* Eliminate spaces before the field name */ 66 | while (*p == ' ') 67 | p++; 68 | 69 | /* Split the name from the value */ 70 | delim = strchr(p, '='); 71 | if (! delim) 72 | goto next; 73 | else { 74 | int len; char *value; char *name; 75 | name = p; 76 | *delim = '\0'; 77 | value = delim + 1; 78 | len = strlen(value); 79 | 80 | /* Remove quotes */ 81 | if (value[0] == '"' && value[len-1] == '"') { 82 | value[len-1] = '\0'; 83 | value++; 84 | } 85 | 86 | if (! strcmp(name, "filename")) { 87 | free(localbuf); 88 | return strdup(value); 89 | } 90 | } 91 | 92 | next: 93 | tmp = NULL; 94 | } 95 | 96 | free(localbuf); 97 | return NULL; 98 | } 99 | 100 | char *fwupgrade_cgi_receive_data(unsigned int *length_out) 101 | { 102 | char *method; 103 | char *content_type; 104 | char *content_length; 105 | long length, length_read; 106 | char *buffer = NULL; 107 | char *boundary = NULL, *boundary_start, *data, *cur; 108 | unsigned int boundary_len, data_len, remaining; 109 | int boundary_found; 110 | char *filename; 111 | 112 | method = getenv("REQUEST_METHOD"); 113 | if (! method) { 114 | printf("ERROR: incorrect REQUEST_METHOD, aborting.\n"); 115 | goto error; 116 | } 117 | 118 | if (strcasecmp(method, "post")) { 119 | printf("ERROR: incorrect HTTP method, aborting.\n"); 120 | goto error; 121 | } 122 | 123 | content_type = getenv("CONTENT_TYPE"); 124 | if (! content_type) { 125 | printf("ERROR: no content type, aborting.\n"); 126 | goto error; 127 | } 128 | 129 | content_length = getenv("CONTENT_LENGTH"); 130 | if (! content_length) { 131 | printf("ERROR: no content length, aborting.\n"); 132 | goto error; 133 | } 134 | 135 | /* Verify that we have a supported content type */ 136 | if (strncasecmp(content_type, VALID_CONTENT_TYPE, 137 | strlen(VALID_CONTENT_TYPE))) { 138 | printf("ERROR: unsupported content type %s, aborting.\n", 139 | content_type); 140 | goto error; 141 | } 142 | 143 | boundary_start = strchr(content_type, '='); 144 | if (! boundary_start) { 145 | printf("ERROR: cannot find boundary delimiter, aborting.\n"); 146 | goto error; 147 | } 148 | 149 | /* Skip the '=' character */ 150 | boundary_start += 1; 151 | 152 | boundary_len = 2 + strlen(boundary_start); 153 | boundary = malloc(boundary_len + 1); 154 | if (! boundary) { 155 | printf("ERROR: memory allocation problem, aborting.\n"); 156 | goto error; 157 | } 158 | 159 | snprintf(boundary, boundary_len + 1, "--%s", boundary_start); 160 | 161 | length = strtol(content_length, NULL, 10); 162 | if (length == LONG_MIN || length == LONG_MAX) { 163 | printf("ERROR: incorrect length\n"); 164 | goto error; 165 | } 166 | 167 | buffer = malloc(length); 168 | if (! buffer) { 169 | printf("ERROR: memory allocation problem, aborting.\n"); 170 | goto error; 171 | } 172 | 173 | length_read = fread(buffer, 1, length, stdin); 174 | if (length_read != length) { 175 | printf("ERROR: could not read the complete %ld bytes, aborting.\n", length); 176 | goto error; 177 | } 178 | 179 | remaining = length; 180 | cur = buffer; 181 | 182 | /* The data should start with the boundary delimiter */ 183 | if (strncmp(boundary, cur, boundary_len)) { 184 | printf("ERROR: cannot find boundary delimiter in data, aborting.\n"); 185 | goto error; 186 | } 187 | 188 | cur = nextline(cur, & remaining); 189 | 190 | /* Check that we have a Content-Disposition line */ 191 | if (strncmp(cur, VALID_ELEMENT_CONTENT_DISPOSITION, 192 | strlen(VALID_ELEMENT_CONTENT_DISPOSITION))) { 193 | printf("ERROR: cannot find Content-Disposition in element\n"); 194 | goto error; 195 | } 196 | 197 | filename = get_filename_from_content_disposition(cur); 198 | 199 | cur = nextline(cur, & remaining); 200 | 201 | /* Check that we have a Content-Type line */ 202 | if (strncmp(cur, VALID_ELEMENT_CONTENT_TYPE, 203 | strlen(VALID_ELEMENT_CONTENT_TYPE))) { 204 | printf("ERROR: cannot find Content-Type in element\n"); 205 | goto error; 206 | } 207 | 208 | /* Skip all lines until we find an empty line */ 209 | while((cur = nextline(cur, & remaining)) != NULL) { 210 | if (cur[0] == '\r' && cur[1] == '\n') { 211 | cur = nextline(cur, & remaining); 212 | break; 213 | } 214 | } 215 | 216 | if (cur == NULL) { 217 | printf("ERROR: cannot find data in firmware image\n"); 218 | goto error; 219 | } 220 | 221 | /* The real data starts here */ 222 | data = cur; 223 | 224 | data_len = 0; 225 | boundary_found = 0; 226 | while(remaining >= boundary_len) { 227 | /* Have we reached the boundary, which marks the end 228 | of the data ? */ 229 | if (! strncmp(cur, boundary, boundary_len)) { 230 | if (! strncmp(data + data_len - 2, "\r\n", 2)) 231 | data_len -= 2; 232 | boundary_found = 1; 233 | break; 234 | } 235 | 236 | data_len ++; 237 | cur ++; 238 | remaining --; 239 | } 240 | 241 | if (! boundary_found) { 242 | printf("ERROR: cannot find boundary\n"); 243 | goto error; 244 | } 245 | 246 | printf("Received image file '%s' of %d bytes\n", 247 | filename, data_len); 248 | 249 | /* Move the useful data at the beginning of the buffer, so 250 | that the beginning of the data is 4 bytes aligned */ 251 | memmove(buffer, data, data_len); 252 | *length_out = data_len; 253 | free(boundary); 254 | 255 | return buffer; 256 | 257 | error: 258 | free(buffer); 259 | free(boundary); 260 | return NULL; 261 | } 262 | -------------------------------------------------------------------------------- /fwupgrade-cgi.h: -------------------------------------------------------------------------------- 1 | #ifndef __FWUPGRADE_CGI_H__ 2 | #define __FWUPGRADE_CGI_H__ 3 | 4 | char *fwupgrade_cgi_receive_data(unsigned int *length_out); 5 | 6 | #endif /* __FWUPGRADE_CGI_H__ */ 7 | -------------------------------------------------------------------------------- /fwupgrade-doc.txt: -------------------------------------------------------------------------------- 1 | Firmware upgrade 2 | ================ 3 | 4 | This software is composed of two applications : 5 | 6 | * fwupgrade-tool, which is an utility compiled for the host machine, 7 | that allows to generate and inspect a firmware image 8 | 9 | * fwupgrade, which is an executable typically compiled for the target 10 | and having two roles: 11 | 12 | When called with just the name 'fwupgrade' and a file name as 13 | argument, it triggers the firmware upgrade process using a locally 14 | stored firmware image file. 15 | 16 | When called with the name 'fwupgrade-cgi', it acts as a cgi-bin 17 | executable, that receives the firmware image from HTTP and then 18 | runs the firmware upgrade process. 19 | 20 | In both cases, the firmware upgrade process will flash the various 21 | parts of the firmware image in the right MTD partitions/UBIFS volumes 22 | and will update the U-Boot environment accordingly 23 | 24 | Typically, the 'fwupgrade' program is installed in /usr/bin, and 25 | for the CGI side, a symbolic link from for example 26 | /var/www/cgi-bin/fwupgrade-cgi to /usr/bin/fwupgrade is used. 27 | 28 | The firmware image contains a set of "parts", each identified by a 29 | name. The fwupgrade-cgi program contains a configuration telling where 30 | a given part should be flashed. For each part, two MTD partitions (or 31 | UBIFS volumes) are specified, so that the updates are not done by 32 | overwriting the currently running kernel or the currently mounted 33 | filesystems. The fwupgrade-cgi looks at the U-Boot environment for 34 | variables named _mtdpart (or _ubivol) to know where 35 | the current version of the part is being stored. 36 | 37 | As this tool handles MTD or UBI partitions, a fourth field must be 38 | defined to indicate if it is a MTD or UBI partition type. In case this 39 | field is not defined, it will assume MTD partitions by default. 40 | 41 | ** Example for MTD partitions ** 42 | 43 | For exemple, if a part is named "rootfs" and the fwupgrade-cgi program 44 | has the configuration that the "rootfs" part is on mtd2 or mtd3, then: 45 | 46 | * If the rootfs_mtdpart U-Boot variable says 'mtd2', the new image is 47 | flashed on mtd3 and the U-Boot variable rootfs_mtdpart is changed 48 | to 'mtd3' 49 | 50 | * If the rootfs_mtdpart U-Boot variable says 'mtd3', the new image is 51 | flashed on mtd2 and the U-Boot variable rootfs_mtdpart is changed 52 | to 'mtd2' 53 | 54 | Of course, the U-Boot environnement is supposed to be configured so 55 | that the rootfs_mtdpart is used to tell the kernel where the current 56 | root filesystem is. 57 | 58 | ** Example for UBIFS volumes ** 59 | 60 | In case of a UBI/UBIFS system, the fwupgrade tool will update UBIFS 61 | volumes. In that case, it follows the same description than above with 62 | some differences: 63 | * You will specify a volume name and not a MTD partition name 64 | * You *must* define the 4th field to indicate that it is a UBI 65 | partition type with "ubi" keyword. 66 | * The u-boot environment variable is no longer "_mtdpart" but 67 | "_ubivol". 68 | 69 | See "fwupgrade-ubi-example.conf" file to have an example of a UBI 70 | configuration. 71 | 72 | Firmware image file format 73 | ========================== 74 | 75 | +------------+ --------------------------- +--------------+ 76 | | header | | magic | 77 | +------------+ \ | hwid | 78 | | | \ | flags | 79 | | part 0 | \ +--------------+ ----------- +-------------+ 80 | | | \ | part 0 desc | | name | 81 | +------------+ \ +--------------+ \ | crc | 82 | | | \ | part 1 desc | \ | length | 83 | | | \ +--------------+ \ | offset | 84 | | part 1 | \ | part 2 desc | \ | padding | 85 | | | \ +--------------+ \______ +-------------+ 86 | | | \ | part 3 desc | 128 bytes 87 | +------------+ \ +--------------+ 88 | | | \ | part 4 desc | 89 | | | \ +--------------+ 90 | | | \ ........... 91 | | | \ +--------------+ 92 | | part 2 | \ | part 15 desc | 93 | | | \ +--------------+ 94 | | | \ | padding | 95 | | | \________ +--------------+ 96 | | | 2048 bytes 97 | +------------+ 98 | | | 99 | | | 100 | | part 3 | 101 | | | 102 | | | 103 | +------------+ 104 | 105 | Testing 106 | ======= 107 | 108 | To test the firmware upgrade process, it is nice to have this curl 109 | command around: 110 | 111 | curl -F "file=@./firmware.img" http://IPADDR/cgi-bin/fwupgrade-cgi 112 | -------------------------------------------------------------------------------- /fwupgrade-file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fwupgrade-file.h" 8 | 9 | char *fwupgrade_load_file_data(const char *filename, unsigned int *length_out) 10 | { 11 | int fd; 12 | struct stat st; 13 | 14 | if (! filename) 15 | return NULL; 16 | 17 | fd = open(filename, O_RDONLY); 18 | if (fd < 0) 19 | return NULL; 20 | 21 | if (fstat(fd, &st)) 22 | return NULL; 23 | 24 | return mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 25 | } 26 | -------------------------------------------------------------------------------- /fwupgrade-file.h: -------------------------------------------------------------------------------- 1 | #ifndef __FWUPGRADE_FILE_H__ 2 | #define __FWUPGRADE_FILE_H__ 3 | 4 | char *fwupgrade_load_file_data(const char *filename, unsigned int *length_out); 5 | 6 | #endif /* __FWUPGRADE_CGI_H__ */ 7 | -------------------------------------------------------------------------------- /fwupgrade-tool.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "fwupgrade.h" 12 | 13 | #define MODE_DUMP 0x42 14 | #define MODE_EXTRACT 0x43 15 | 16 | int dump_or_extract_file(const char *filename, int mode) 17 | { 18 | void *addr; 19 | struct stat s; 20 | int ret, fd, i; 21 | struct fwheader *header; 22 | 23 | ret = stat(filename, &s); 24 | if (ret) { 25 | fprintf(stderr, "Error while stat()ing file: %m\n"); 26 | return -1; 27 | } 28 | 29 | fd = open(filename, O_RDONLY);; 30 | if (fd < 0) { 31 | fprintf(stderr, "Error while opening file: %m\n"); 32 | return -1; 33 | } 34 | 35 | addr = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 36 | if (addr == MAP_FAILED) { 37 | fprintf(stderr, "Error while mapping file: %m\n"); 38 | close(fd); 39 | return -1; 40 | } 41 | 42 | header = addr; 43 | 44 | if (le32toh(header->magic) != FWUPGRADE_MAGIC) { 45 | fprintf(stderr, "Unrecognized firmware file, invalid magic\n"); 46 | return -1; 47 | } 48 | 49 | if (mode == MODE_DUMP) { 50 | printf("HWID : 0x%x\n", le32toh(header->hwid)); 51 | printf("Flags : 0x%x\n", le32toh(header->flags)); 52 | } 53 | 54 | for (i = 0; i < FWPART_COUNT; i++) { 55 | unsigned int sz, offset; 56 | char computed_crc[FWPART_CRC_SZ]; 57 | 58 | sz = le32toh(header->parts[i].length); 59 | offset = le32toh(header->parts[i].offset); 60 | if (! sz) 61 | continue; 62 | 63 | md5(addr + offset, sz, computed_crc); 64 | if (memcmp(computed_crc, header->parts[i].crc, FWPART_CRC_SZ)) { 65 | fprintf(stderr, "CRC for part %d do not match\n", i); 66 | return -1; 67 | } 68 | 69 | if (mode == MODE_DUMP) { 70 | printf("part[%d] : name=%s, size=%d, offset=%d\n", 71 | i, header->parts[i].name, sz, offset); 72 | } 73 | else if (mode == MODE_EXTRACT) { 74 | char *extracted_file_name; 75 | FILE *extracted_file; 76 | 77 | if (asprintf(& extracted_file_name, "extracted-%s.img", 78 | header->parts[i].name) < 0) 79 | { 80 | fprintf(stderr, "Cannot allocate memory\n"); 81 | exit(1); 82 | } 83 | 84 | extracted_file = fopen(extracted_file_name, "w+"); 85 | if (! extracted_file) { 86 | fprintf(stderr, "Cannot open output file %s\n", 87 | extracted_file_name); 88 | free(extracted_file_name); 89 | exit(1); 90 | } 91 | 92 | if (fwrite(addr + offset, sz, 1, extracted_file) != 1) { 93 | fprintf(stderr, "Cannot write output file %s\n", 94 | extracted_file_name); 95 | free(extracted_file_name); 96 | exit(1); 97 | } 98 | 99 | fclose(extracted_file); 100 | free(extracted_file_name); 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | void help(void) 108 | { 109 | printf("fwupgrade-tool, create and dump firmware images\n"); 110 | printf(" image creation: fwupgrade-tool -o output-file -p part1name:part1file -p part2name:part2file -i HWID\n"); 111 | printf(" image dump : fwupgrade-tool -d image-file\n"); 112 | printf(" image extract : fwupgrade-tool -x image-file\n"); 113 | } 114 | 115 | int main(int argc, char *argv[]) 116 | { 117 | int opt; 118 | unsigned int hwid = 0; 119 | 120 | /* Contains the name:filename list of strings, as passed by 121 | the user using the -p option */ 122 | char *parts[FWPART_COUNT]; 123 | 124 | /* Contains the memory addresses at which each part has been 125 | mapped */ 126 | void *parts_addrs[FWPART_COUNT]; 127 | int part_count = 0; 128 | int i, ret; 129 | 130 | char *output = NULL; 131 | char *dumpfile = NULL; 132 | char *extractfile = NULL; 133 | struct fwheader header; 134 | unsigned int current_offset = sizeof(struct fwheader); 135 | int verbose = 0; 136 | 137 | memset(parts, 0, sizeof(parts)); 138 | memset(parts_addrs, 0, sizeof(parts_addrs)); 139 | 140 | /* Analyze the options. We fill the "hwid" variable and the 141 | "parts" array. */ 142 | while ((opt = getopt(argc, argv, "hi:p:o:d:x:v")) != -1) { 143 | switch(opt) { 144 | case 'h': 145 | help(); 146 | exit(0); 147 | case 'i': 148 | hwid = strtol(optarg, NULL, 16); 149 | break; 150 | 151 | case 'p': 152 | if (part_count >= FWPART_COUNT) { 153 | fprintf(stderr, "Too many parts\n"); 154 | exit(1); 155 | } 156 | parts[part_count] = strdup(optarg); 157 | part_count++; 158 | break; 159 | case 'o': 160 | output = strdup(optarg); 161 | break; 162 | case 'd': 163 | dumpfile = strdup(optarg); 164 | break; 165 | case 'x': 166 | extractfile = strdup(optarg); 167 | break; 168 | case 'v': 169 | verbose = 1; 170 | break; 171 | default: 172 | fprintf(stderr, "Unknown option\n"); 173 | exit(1); 174 | } 175 | } 176 | 177 | if (dumpfile && extractfile) { 178 | fprintf(stderr, "Option -d and -x are mutually exclusive\n"); 179 | help(); 180 | exit(1); 181 | } 182 | 183 | if (dumpfile) { 184 | return dump_or_extract_file(dumpfile, MODE_DUMP); 185 | } 186 | 187 | if (extractfile) { 188 | return dump_or_extract_file(extractfile, MODE_EXTRACT); 189 | } 190 | 191 | if (part_count == 0) { 192 | fprintf(stderr, "No parts given, aborting\n"); 193 | help(); 194 | exit(1); 195 | } 196 | 197 | if (hwid == 0) { 198 | fprintf(stderr, "No HWID given, aborting\n"); 199 | help(); 200 | exit(1); 201 | } 202 | 203 | if (! output) { 204 | fprintf(stderr, "No output file given, aborting\n"); 205 | help(); 206 | exit(1); 207 | } 208 | 209 | memset(& header, 0, sizeof(struct fwheader)); 210 | 211 | header.magic = htole32(FWUPGRADE_MAGIC); 212 | header.hwid = htole32(hwid); 213 | header.flags = htole32(0); 214 | 215 | /* For each part, we get the part size, map the part into 216 | memory, calculate its MD5 checksum and we fill the header 217 | with those informations. */ 218 | 219 | for (i = 0; i < part_count; i++) { 220 | struct stat s; 221 | int fd; 222 | char *filename, *tmp; 223 | int name_len; 224 | 225 | /* First, we extract the name:filename informations */ 226 | tmp = strchr(parts[i], ':'); 227 | if (! tmp) { 228 | fprintf(stderr, "Incorrect part syntax: '%s'\n", 229 | parts[i]); 230 | exit (1); 231 | } 232 | 233 | name_len = tmp - parts[i]; 234 | 235 | if (name_len + 1 > FWPART_NAME_SZ) { 236 | fprintf(stderr, "Name too long in part: '%s'\n", 237 | parts[i]); 238 | exit(1); 239 | } 240 | 241 | /* Fill the name information of the part header */ 242 | strncpy(header.parts[i].name, parts[i], name_len); 243 | header.parts[i].name[name_len] = '\0'; 244 | 245 | /* Skip the ':' to get the filename */ 246 | filename = tmp + 1; 247 | 248 | /* Get the file size */ 249 | ret = stat(filename, & s); 250 | if (ret) { 251 | fprintf(stderr, "Cannot find part '%s'\n", parts[i]); 252 | exit(1); 253 | } 254 | 255 | /* Store the size in the header */ 256 | header.parts[i].length = htole32(s.st_size); 257 | header.parts[i].offset = htole32(current_offset); 258 | current_offset += s.st_size; 259 | 260 | /* Open and map the file */ 261 | fd = open(filename, O_RDONLY); 262 | if (fd < 0) { 263 | fprintf(stderr, "Cannot open part '%s'\n", parts[i]); 264 | exit(1); 265 | } 266 | 267 | parts_addrs[i] = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 268 | if (parts_addrs[i] == MAP_FAILED) { 269 | fprintf(stderr, "Cannot map part '%s'\n", parts[i]); 270 | exit(1); 271 | } 272 | 273 | /* Compute its MD5 */ 274 | md5(parts_addrs[i], header.parts[i].length, 275 | header.parts[i].crc); 276 | 277 | if (verbose) 278 | printf("part[%d], name=%s, filename=%s, size=%d, offset=%d, md5=%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx%hhx\n", 279 | i, header.parts[i].name, filename, header.parts[i].length, header.parts[i].offset, 280 | header.parts[i].crc[0], header.parts[i].crc[1], 281 | header.parts[i].crc[2], header.parts[i].crc[3], 282 | header.parts[i].crc[4], header.parts[i].crc[5], 283 | header.parts[i].crc[6], header.parts[i].crc[7], 284 | header.parts[i].crc[8], header.parts[i].crc[9], 285 | header.parts[i].crc[10], header.parts[i].crc[11], 286 | header.parts[i].crc[12], header.parts[i].crc[13], 287 | header.parts[i].crc[14], header.parts[i].crc[15]); 288 | } 289 | 290 | /* Write data to the output file: first the header, then each 291 | part */ 292 | FILE *outfile = fopen(output, "w+"); 293 | fwrite(& header, 1, sizeof(header), outfile); 294 | for (i = 0; i < part_count; i++) { 295 | fwrite(parts_addrs[i], 1, header.parts[i].length, outfile); 296 | } 297 | fclose(outfile); 298 | 299 | return 0; 300 | } 301 | -------------------------------------------------------------------------------- /fwupgrade-ubi-example.conf: -------------------------------------------------------------------------------- 1 | rootfs:rootfs1:rootfs2:ubi -------------------------------------------------------------------------------- /fwupgrade-uboot-env.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2000-2010 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 | * 5 | * (C) Copyright 2008 6 | * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de. 7 | * 8 | * See file CREDITS for list of people who contributed to this 9 | * project. 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program; if not, write to the Free Software 23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 24 | * MA 02111-1307 USA 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef MTD_OLD 40 | # include 41 | #else 42 | # define __user /* nothing */ 43 | # include 44 | #endif 45 | 46 | #include "fwupgrade-uboot-env.h" 47 | 48 | #define WHITESPACE(c) ((c == '\t') || (c == ' ')) 49 | 50 | #define min(x, y) ({ \ 51 | typeof(x) _min1 = (x); \ 52 | typeof(y) _min2 = (y); \ 53 | (void) (&_min1 == &_min2); \ 54 | _min1 < _min2 ? _min1 : _min2; }) 55 | 56 | struct envdev_s { 57 | char devname[64]; /* Device name */ 58 | ulong devoff; /* Device offset */ 59 | ulong env_size; /* environment size */ 60 | ulong erase_size; /* device erase size */ 61 | ulong env_sectors; /* number of environment sectors */ 62 | uint8_t mtd_type; /* type of the MTD device */ 63 | }; 64 | 65 | static struct envdev_s envdevices[2] = 66 | { 67 | { 68 | .mtd_type = MTD_ABSENT, 69 | }, { 70 | .mtd_type = MTD_ABSENT, 71 | }, 72 | }; 73 | static int dev_current; 74 | 75 | #define DEVNAME(i) envdevices[(i)].devname 76 | #define DEVOFFSET(i) envdevices[(i)].devoff 77 | #define ENVSIZE(i) envdevices[(i)].env_size 78 | #define DEVESIZE(i) envdevices[(i)].erase_size 79 | #define ENVSECTORS(i) envdevices[(i)].env_sectors 80 | #define DEVTYPE(i) envdevices[(i)].mtd_type 81 | 82 | #define CONFIG_ENV_SIZE ENVSIZE(dev_current) 83 | 84 | #define ENV_SIZE getenvsize() 85 | 86 | struct env_image_single { 87 | uint32_t crc; /* CRC32 over data bytes */ 88 | char data[]; 89 | }; 90 | 91 | struct env_image_redundant { 92 | uint32_t crc; /* CRC32 over data bytes */ 93 | unsigned char flags; /* active or obsolete */ 94 | char data[]; 95 | }; 96 | 97 | enum flag_scheme { 98 | FLAG_NONE, 99 | FLAG_BOOLEAN, 100 | FLAG_INCREMENTAL, 101 | }; 102 | 103 | struct environment { 104 | void *image; 105 | uint32_t *crc; 106 | unsigned char *flags; 107 | char *data; 108 | enum flag_scheme flag_scheme; 109 | }; 110 | 111 | static struct environment environment = { 112 | .flag_scheme = FLAG_NONE, 113 | }; 114 | 115 | static int HaveRedundEnv = 0; 116 | 117 | static unsigned char active_flag = 1; 118 | /* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */ 119 | static unsigned char obsolete_flag = 0; 120 | 121 | 122 | #define XMK_STR(x) #x 123 | #define MK_STR(x) XMK_STR(x) 124 | 125 | static char default_environment[] = { 126 | #if defined(CONFIG_BOOTARGS) 127 | "bootargs=" CONFIG_BOOTARGS "\0" 128 | #endif 129 | #if defined(CONFIG_BOOTCOMMAND) 130 | "bootcmd=" CONFIG_BOOTCOMMAND "\0" 131 | #endif 132 | #if defined(CONFIG_RAMBOOTCOMMAND) 133 | "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" 134 | #endif 135 | #if defined(CONFIG_NFSBOOTCOMMAND) 136 | "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" 137 | #endif 138 | #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 139 | "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" 140 | #endif 141 | #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) 142 | "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" 143 | #endif 144 | #ifdef CONFIG_LOADS_ECHO 145 | "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" 146 | #endif 147 | #ifdef CONFIG_ETHADDR 148 | "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" 149 | #endif 150 | #ifdef CONFIG_ETH1ADDR 151 | "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" 152 | #endif 153 | #ifdef CONFIG_ETH2ADDR 154 | "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" 155 | #endif 156 | #ifdef CONFIG_ETH3ADDR 157 | "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" 158 | #endif 159 | #ifdef CONFIG_ETH4ADDR 160 | "eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0" 161 | #endif 162 | #ifdef CONFIG_ETH5ADDR 163 | "eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0" 164 | #endif 165 | #ifdef CONFIG_ETHPRIME 166 | "ethprime=" CONFIG_ETHPRIME "\0" 167 | #endif 168 | #ifdef CONFIG_IPADDR 169 | "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" 170 | #endif 171 | #ifdef CONFIG_SERVERIP 172 | "serverip=" MK_STR (CONFIG_SERVERIP) "\0" 173 | #endif 174 | #ifdef CONFIG_SYS_AUTOLOAD 175 | "autoload=" CONFIG_SYS_AUTOLOAD "\0" 176 | #endif 177 | #ifdef CONFIG_ROOTPATH 178 | "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" 179 | #endif 180 | #ifdef CONFIG_GATEWAYIP 181 | "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" 182 | #endif 183 | #ifdef CONFIG_NETMASK 184 | "netmask=" MK_STR (CONFIG_NETMASK) "\0" 185 | #endif 186 | #ifdef CONFIG_HOSTNAME 187 | "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" 188 | #endif 189 | #ifdef CONFIG_BOOTFILE 190 | "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" 191 | #endif 192 | #ifdef CONFIG_LOADADDR 193 | "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" 194 | #endif 195 | #ifdef CONFIG_PREBOOT 196 | "preboot=" CONFIG_PREBOOT "\0" 197 | #endif 198 | #ifdef CONFIG_CLOCKS_IN_MHZ 199 | "clocks_in_mhz=" "1" "\0" 200 | #endif 201 | #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) 202 | "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" 203 | #endif 204 | #ifdef CONFIG_EXTRA_ENV_SETTINGS 205 | CONFIG_EXTRA_ENV_SETTINGS 206 | #endif 207 | "\0" /* Termimate struct environment data with 2 NULs */ 208 | }; 209 | 210 | static int flash_io (int mode); 211 | static char *envmatch (char * s1, char * s2); 212 | static int parse_config (void); 213 | 214 | #if defined(CONFIG_FILE) 215 | static int get_config (char *); 216 | #endif 217 | static inline ulong getenvsize (void) 218 | { 219 | ulong rc = CONFIG_ENV_SIZE - sizeof (long); 220 | 221 | if (HaveRedundEnv) 222 | rc -= sizeof (char); 223 | return rc; 224 | } 225 | 226 | static char *fw_string_blank(char *s, int noblank) 227 | { 228 | int i; 229 | int len = strlen(s); 230 | 231 | for (i = 0; i < len; i++, s++) { 232 | if ((noblank && !WHITESPACE(*s)) || 233 | (!noblank && WHITESPACE(*s))) 234 | break; 235 | } 236 | if (i == len) 237 | return NULL; 238 | 239 | return s; 240 | } 241 | 242 | char *fw_env_read(char *name) 243 | { 244 | char *env, *nxt; 245 | 246 | for (env = environment.data; *env; env = nxt + 1) { 247 | char *val; 248 | 249 | for (nxt = env; *nxt; ++nxt) { 250 | if (nxt >= &environment.data[ENV_SIZE]) { 251 | fprintf (stderr, "## Error: " 252 | "environment not terminated\n"); 253 | return NULL; 254 | } 255 | } 256 | val = envmatch (name, env); 257 | if (!val) 258 | continue; 259 | return val; 260 | } 261 | 262 | return NULL; 263 | } 264 | 265 | /* 266 | * Search the environment for a variable. 267 | * Return the value, if found, or NULL, if not found. 268 | */ 269 | char *fw_getenv (char *name) 270 | { 271 | if (fw_env_open()) 272 | return NULL; 273 | 274 | return fw_env_read(name); 275 | } 276 | 277 | /* 278 | * Print the current definition of one, or more, or all 279 | * environment variables 280 | */ 281 | int fw_printenv (int argc, char *argv[]) 282 | { 283 | char *env, *nxt; 284 | int i, n_flag; 285 | int rc = 0; 286 | 287 | if (fw_env_open()) 288 | return -1; 289 | 290 | if (argc == 1) { /* Print all env variables */ 291 | for (env = environment.data; *env; env = nxt + 1) { 292 | for (nxt = env; *nxt; ++nxt) { 293 | if (nxt >= &environment.data[ENV_SIZE]) { 294 | fprintf (stderr, "## Error: " 295 | "environment not terminated\n"); 296 | return -1; 297 | } 298 | } 299 | 300 | printf ("%s\n", env); 301 | } 302 | return 0; 303 | } 304 | 305 | if (strcmp (argv[1], "-n") == 0) { 306 | n_flag = 1; 307 | ++argv; 308 | --argc; 309 | if (argc != 2) { 310 | fprintf (stderr, "## Error: " 311 | "`-n' option requires exactly one argument\n"); 312 | return -1; 313 | } 314 | } else { 315 | n_flag = 0; 316 | } 317 | 318 | for (i = 1; i < argc; ++i) { /* print single env variables */ 319 | char *name = argv[i]; 320 | char *val = NULL; 321 | 322 | for (env = environment.data; *env; env = nxt + 1) { 323 | 324 | for (nxt = env; *nxt; ++nxt) { 325 | if (nxt >= &environment.data[ENV_SIZE]) { 326 | fprintf (stderr, "## Error: " 327 | "environment not terminated\n"); 328 | return -1; 329 | } 330 | } 331 | val = envmatch (name, env); 332 | if (val) { 333 | if (!n_flag) { 334 | fputs (name, stdout); 335 | putc ('=', stdout); 336 | } 337 | puts (val); 338 | break; 339 | } 340 | } 341 | if (!val) { 342 | fprintf (stderr, "## Error: \"%s\" not defined\n", name); 343 | rc = -1; 344 | } 345 | } 346 | 347 | return rc; 348 | } 349 | 350 | int fw_env_close(void) 351 | { 352 | /* 353 | * Update CRC 354 | */ 355 | *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); 356 | 357 | /* write environment back to flash */ 358 | if (flash_io(O_RDWR)) { 359 | fprintf(stderr, 360 | "Error: can't write fw_env to flash\n"); 361 | return -1; 362 | } 363 | 364 | return 0; 365 | } 366 | 367 | 368 | /* 369 | * Set/Clear a single variable in the environment. 370 | * This is called in sequence to update the environment 371 | * in RAM without updating the copy in flash after each set 372 | */ 373 | int fw_env_write(char *name, char *value) 374 | { 375 | int len; 376 | char *env, *nxt; 377 | char *oldval = NULL; 378 | 379 | /* 380 | * search if variable with this name already exists 381 | */ 382 | for (nxt = env = environment.data; *env; env = nxt + 1) { 383 | for (nxt = env; *nxt; ++nxt) { 384 | if (nxt >= &environment.data[ENV_SIZE]) { 385 | fprintf(stderr, "## Error: " 386 | "environment not terminated\n"); 387 | errno = EINVAL; 388 | return -1; 389 | } 390 | } 391 | if ((oldval = envmatch (name, env)) != NULL) 392 | break; 393 | } 394 | 395 | /* 396 | * Delete any existing definition 397 | */ 398 | if (oldval) { 399 | /* 400 | * Ethernet Address and serial# can be set only once 401 | */ 402 | if ((strcmp (name, "ethaddr") == 0) || 403 | (strcmp (name, "serial#") == 0)) { 404 | fprintf (stderr, "Can't overwrite \"%s\"\n", name); 405 | errno = EROFS; 406 | return -1; 407 | } 408 | 409 | if (*++nxt == '\0') { 410 | *env = '\0'; 411 | } else { 412 | for (;;) { 413 | *env = *nxt++; 414 | if ((*env == '\0') && (*nxt == '\0')) 415 | break; 416 | ++env; 417 | } 418 | } 419 | *++env = '\0'; 420 | } 421 | 422 | /* Delete only ? */ 423 | if (!value || !strlen(value)) 424 | return 0; 425 | 426 | /* 427 | * Append new definition at the end 428 | */ 429 | for (env = environment.data; *env || *(env + 1); ++env); 430 | if (env > environment.data) 431 | ++env; 432 | /* 433 | * Overflow when: 434 | * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment) 435 | */ 436 | len = strlen (name) + 2; 437 | /* add '=' for first arg, ' ' for all others */ 438 | len += strlen(value) + 1; 439 | 440 | if (len > (&environment.data[ENV_SIZE] - env)) { 441 | fprintf (stderr, 442 | "Error: environment overflow, \"%s\" deleted\n", 443 | name); 444 | return -1; 445 | } 446 | 447 | while ((*env = *name++) != '\0') 448 | env++; 449 | *env = '='; 450 | while ((*++env = *value++) != '\0') 451 | ; 452 | 453 | /* end is marked with double '\0' */ 454 | *++env = '\0'; 455 | 456 | return 0; 457 | } 458 | 459 | /* 460 | * Deletes or sets environment variables. Returns -1 and sets errno error codes: 461 | * 0 - OK 462 | * EINVAL - need at least 1 argument 463 | * EROFS - certain variables ("ethaddr", "serial#") cannot be 464 | * modified or deleted 465 | * 466 | */ 467 | int fw_setenv(int argc, char *argv[]) 468 | { 469 | int i, len; 470 | char *name; 471 | char *value = NULL; 472 | char *tmpval = NULL; 473 | 474 | if (argc < 2) { 475 | errno = EINVAL; 476 | return -1; 477 | } 478 | 479 | if (fw_env_open()) { 480 | fprintf(stderr, "Error: environment not initialized\n"); 481 | return -1; 482 | } 483 | 484 | name = argv[1]; 485 | 486 | len = strlen(name) + 2; 487 | for (i = 2; i < argc; ++i) 488 | len += strlen(argv[i]) + 1; 489 | 490 | /* Allocate enough place to the data string */ 491 | for (i = 2; i < argc; ++i) { 492 | char *val = argv[i]; 493 | if (!value) { 494 | value = (char *)malloc(len - strlen(name)); 495 | if (!value) { 496 | fprintf(stderr, 497 | "Cannot malloc %u bytes: %s\n", 498 | (unsigned) (len - strlen(name)), strerror(errno)); 499 | return -1; 500 | } 501 | memset(value, 0, len - strlen(name)); 502 | tmpval = value; 503 | } 504 | if (i != 2) 505 | *tmpval++ = ' '; 506 | while (*val != '\0') 507 | *tmpval++ = *val++; 508 | } 509 | 510 | fw_env_write(name, value); 511 | 512 | if (value) 513 | free(value); 514 | 515 | return fw_env_close(); 516 | } 517 | 518 | /* 519 | * Parse a file and configure the u-boot variables. 520 | * The script file has a very simple format, as follows: 521 | * 522 | * Each line has a couple with name, value: 523 | * variable_namevariable_value 524 | * 525 | * Both variable_name and variable_value are interpreted as strings. 526 | * Any character after and before ending \r\n is interpreted 527 | * as variable's value (no comment allowed on these lines !) 528 | * 529 | * Comments are allowed if the first character in the line is # 530 | * 531 | * Returns -1 and sets errno error codes: 532 | * 0 - OK 533 | * -1 - Error 534 | */ 535 | int fw_parse_script(char *fname) 536 | { 537 | FILE *fp; 538 | char dump[1024]; /* Maximum line length in the file */ 539 | char *name; 540 | char *val; 541 | int lineno = 0; 542 | int len; 543 | int ret = 0; 544 | 545 | if (fw_env_open()) { 546 | fprintf(stderr, "Error: environment not initialized\n"); 547 | return -1; 548 | } 549 | 550 | if (strcmp(fname, "-") == 0) 551 | fp = stdin; 552 | else { 553 | fp = fopen(fname, "r"); 554 | if (fp == NULL) { 555 | fprintf(stderr, "I cannot open %s for reading\n", 556 | fname); 557 | return -1; 558 | } 559 | } 560 | 561 | while (fgets(dump, sizeof(dump), fp)) { 562 | lineno++; 563 | len = strlen(dump); 564 | 565 | /* 566 | * Read a whole line from the file. If the line is too long 567 | * or is not terminated, reports an error and exit. 568 | */ 569 | if (dump[len - 1] != '\n') { 570 | fprintf(stderr, 571 | "Line %d not corrected terminated or too long\n", 572 | lineno); 573 | ret = -1; 574 | break; 575 | } 576 | 577 | /* Drop ending line feed / carriage return */ 578 | while (len > 0 && (dump[len - 1] == '\n' || 579 | dump[len - 1] == '\r')) { 580 | dump[len - 1] = '\0'; 581 | len--; 582 | } 583 | 584 | /* Skip comment or empty lines */ 585 | if ((len == 0) || dump[0] == '#') 586 | continue; 587 | 588 | /* 589 | * Search for variable's name, 590 | * remove leading whitespaces 591 | */ 592 | name = fw_string_blank(dump, 1); 593 | if (!name) 594 | continue; 595 | 596 | /* The first white space is the end of variable name */ 597 | val = fw_string_blank(name, 0); 598 | len = strlen(name); 599 | if (val) { 600 | *val++ = '\0'; 601 | if ((val - name) < len) 602 | val = fw_string_blank(val, 1); 603 | else 604 | val = NULL; 605 | } 606 | 607 | #ifdef DEBUG 608 | fprintf(stderr, "Setting %s : %s\n", 609 | name, val ? val : " removed"); 610 | #endif 611 | 612 | /* 613 | * If there is an error setting a variable, 614 | * try to save the environment and returns an error 615 | */ 616 | if (fw_env_write(name, val)) { 617 | fprintf(stderr, 618 | "fw_env_write returns with error : %s\n", 619 | strerror(errno)); 620 | ret = -1; 621 | break; 622 | } 623 | 624 | } 625 | 626 | /* Close file if not stdin */ 627 | if (strcmp(fname, "-") != 0) 628 | fclose(fp); 629 | 630 | ret |= fw_env_close(); 631 | 632 | return ret; 633 | 634 | } 635 | 636 | /* 637 | * Test for bad block on NAND, just returns 0 on NOR, on NAND: 638 | * 0 - block is good 639 | * > 0 - block is bad 640 | * < 0 - failed to test 641 | */ 642 | static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) 643 | { 644 | if (mtd_type == MTD_NANDFLASH) { 645 | int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); 646 | 647 | if (badblock < 0) { 648 | perror ("Cannot read bad block mark"); 649 | return badblock; 650 | } 651 | 652 | if (badblock) { 653 | #ifdef DEBUG 654 | fprintf (stderr, "Bad block at 0x%llx, " 655 | "skipping\n", *blockstart); 656 | #endif 657 | return badblock; 658 | } 659 | } 660 | 661 | return 0; 662 | } 663 | 664 | /* 665 | * Read data from flash at an offset into a provided buffer. On NAND it skips 666 | * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from 667 | * the DEVOFFSET (dev) block. On NOR the loop is only run once. 668 | */ 669 | static int flash_read_buf (int dev, int fd, void *buf, size_t count, 670 | off_t offset, uint8_t mtd_type) 671 | { 672 | size_t blocklen; /* erase / write length - one block on NAND, 673 | 0 on NOR */ 674 | size_t processed = 0; /* progress counter */ 675 | size_t readlen = count; /* current read length */ 676 | off_t top_of_range; /* end of the last block we may use */ 677 | off_t block_seek; /* offset inside the current block to the start 678 | of the data */ 679 | loff_t blockstart; /* running start of the current block - 680 | MEMGETBADBLOCK needs 64 bits */ 681 | int rc; 682 | 683 | blockstart = (offset / DEVESIZE (dev)) * DEVESIZE (dev); 684 | 685 | /* Offset inside a block */ 686 | block_seek = offset - blockstart; 687 | 688 | if (mtd_type == MTD_NANDFLASH) { 689 | /* 690 | * NAND: calculate which blocks we are reading. We have 691 | * to read one block at a time to skip bad blocks. 692 | */ 693 | blocklen = DEVESIZE (dev); 694 | 695 | /* 696 | * To calculate the top of the range, we have to use the 697 | * global DEVOFFSET (dev), which can be different from offset 698 | */ 699 | top_of_range = ((DEVOFFSET(dev) / blocklen) + 700 | ENVSECTORS (dev)) * blocklen; 701 | 702 | /* Limit to one block for the first read */ 703 | if (readlen > blocklen - block_seek) 704 | readlen = blocklen - block_seek; 705 | } else { 706 | blocklen = 0; 707 | top_of_range = offset + count; 708 | } 709 | 710 | /* This only runs once on NOR flash */ 711 | while (processed < count) { 712 | rc = flash_bad_block (fd, mtd_type, &blockstart); 713 | if (rc < 0) /* block test failed */ 714 | return -1; 715 | 716 | if (blockstart + block_seek + readlen > top_of_range) { 717 | /* End of range is reached */ 718 | fprintf (stderr, 719 | "Too few good blocks within range\n"); 720 | return -1; 721 | } 722 | 723 | if (rc) { /* block is bad */ 724 | blockstart += blocklen; 725 | continue; 726 | } 727 | 728 | /* 729 | * If a block is bad, we retry in the next block at the same 730 | * offset - see common/env_nand.c::writeenv() 731 | */ 732 | lseek (fd, blockstart + block_seek, SEEK_SET); 733 | 734 | rc = read (fd, buf + processed, readlen); 735 | if (rc != readlen) { 736 | fprintf (stderr, "Read error on %s: %s\n", 737 | DEVNAME (dev), strerror (errno)); 738 | return -1; 739 | } 740 | #ifdef DEBUG 741 | fprintf (stderr, "Read 0x%x bytes at 0x%llx\n", 742 | rc, blockstart + block_seek); 743 | #endif 744 | processed += readlen; 745 | readlen = min (blocklen, count - processed); 746 | block_seek = 0; 747 | blockstart += blocklen; 748 | } 749 | 750 | return processed; 751 | } 752 | 753 | /* 754 | * Write count bytes at offset, but stay within ENVSECTORS (dev) sectors of 755 | * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we 756 | * erase and write the whole data at once. 757 | */ 758 | static int flash_write_buf (int dev, int fd, void *buf, size_t count, 759 | off_t offset, uint8_t mtd_type) 760 | { 761 | void *data; 762 | struct erase_info_user erase; 763 | size_t blocklen; /* length of NAND block / NOR erase sector */ 764 | size_t erase_len; /* whole area that can be erased - may include 765 | bad blocks */ 766 | size_t erasesize; /* erase / write length - one block on NAND, 767 | whole area on NOR */ 768 | size_t processed = 0; /* progress counter */ 769 | size_t write_total; /* total size to actually write - excluding 770 | bad blocks */ 771 | off_t erase_offset; /* offset to the first erase block (aligned) 772 | below offset */ 773 | off_t block_seek; /* offset inside the erase block to the start 774 | of the data */ 775 | off_t top_of_range; /* end of the last block we may use */ 776 | loff_t blockstart; /* running start of the current block - 777 | MEMGETBADBLOCK needs 64 bits */ 778 | int rc; 779 | 780 | blocklen = DEVESIZE (dev); 781 | 782 | top_of_range = ((DEVOFFSET(dev) / blocklen) + 783 | ENVSECTORS (dev)) * blocklen; 784 | 785 | erase_offset = (offset / blocklen) * blocklen; 786 | 787 | /* Maximum area we may use */ 788 | erase_len = top_of_range - erase_offset; 789 | 790 | blockstart = erase_offset; 791 | /* Offset inside a block */ 792 | block_seek = offset - erase_offset; 793 | 794 | /* 795 | * Data size we actually have to write: from the start of the block 796 | * to the start of the data, then count bytes of data, and to the 797 | * end of the block 798 | */ 799 | write_total = ((block_seek + count + blocklen - 1) / 800 | blocklen) * blocklen; 801 | 802 | /* 803 | * Support data anywhere within erase sectors: read out the complete 804 | * area to be erased, replace the environment image, write the whole 805 | * block back again. 806 | */ 807 | if (write_total > count) { 808 | data = malloc (erase_len); 809 | if (!data) { 810 | fprintf (stderr, 811 | "Cannot malloc %u bytes: %s\n", 812 | (unsigned) erase_len, strerror (errno)); 813 | return -1; 814 | } 815 | 816 | rc = flash_read_buf (dev, fd, data, write_total, erase_offset, 817 | mtd_type); 818 | if (write_total != rc) 819 | return -1; 820 | 821 | /* Overwrite the old environment */ 822 | memcpy (data + block_seek, buf, count); 823 | } else { 824 | /* 825 | * We get here, iff offset is block-aligned and count is a 826 | * multiple of blocklen - see write_total calculation above 827 | */ 828 | data = buf; 829 | } 830 | 831 | if (mtd_type == MTD_NANDFLASH) { 832 | /* 833 | * NAND: calculate which blocks we are writing. We have 834 | * to write one block at a time to skip bad blocks. 835 | */ 836 | erasesize = blocklen; 837 | } else { 838 | erasesize = erase_len; 839 | } 840 | 841 | erase.length = erasesize; 842 | 843 | /* This only runs once on NOR flash and SPI-dataflash */ 844 | while (processed < write_total) { 845 | rc = flash_bad_block (fd, mtd_type, &blockstart); 846 | if (rc < 0) /* block test failed */ 847 | return rc; 848 | 849 | if (blockstart + erasesize > top_of_range) { 850 | fprintf (stderr, "End of range reached, aborting\n"); 851 | return -1; 852 | } 853 | 854 | if (rc) { /* block is bad */ 855 | blockstart += blocklen; 856 | continue; 857 | } 858 | 859 | erase.start = blockstart; 860 | ioctl (fd, MEMUNLOCK, &erase); 861 | 862 | /* Dataflash does not need an explicit erase cycle */ 863 | if (mtd_type != MTD_DATAFLASH) 864 | if (ioctl (fd, MEMERASE, &erase) != 0) { 865 | fprintf (stderr, "MTD erase error on %s: %s\n", 866 | DEVNAME (dev), 867 | strerror (errno)); 868 | return -1; 869 | } 870 | 871 | if (lseek (fd, blockstart, SEEK_SET) == -1) { 872 | fprintf (stderr, 873 | "Seek error on %s: %s\n", 874 | DEVNAME (dev), strerror (errno)); 875 | return -1; 876 | } 877 | 878 | #ifdef DEBUG 879 | printf ("Write 0x%x bytes at 0x%llx\n", erasesize, blockstart); 880 | #endif 881 | if (write (fd, data + processed, erasesize) != erasesize) { 882 | fprintf (stderr, "Write error on %s: %s\n", 883 | DEVNAME (dev), strerror (errno)); 884 | return -1; 885 | } 886 | 887 | ioctl (fd, MEMLOCK, &erase); 888 | 889 | processed += blocklen; 890 | block_seek = 0; 891 | blockstart += blocklen; 892 | } 893 | 894 | if (write_total > count) 895 | free (data); 896 | 897 | return processed; 898 | } 899 | 900 | /* 901 | * Set obsolete flag at offset - NOR flash only 902 | */ 903 | static int flash_flag_obsolete (int dev, int fd, off_t offset) 904 | { 905 | int rc; 906 | struct erase_info_user erase; 907 | 908 | erase.start = DEVOFFSET (dev); 909 | erase.length = DEVESIZE (dev); 910 | /* This relies on the fact, that obsolete_flag == 0 */ 911 | rc = lseek (fd, offset, SEEK_SET); 912 | if (rc < 0) { 913 | fprintf (stderr, "Cannot seek to set the flag on %s \n", 914 | DEVNAME (dev)); 915 | return rc; 916 | } 917 | ioctl (fd, MEMUNLOCK, &erase); 918 | rc = write (fd, &obsolete_flag, sizeof (obsolete_flag)); 919 | ioctl (fd, MEMLOCK, &erase); 920 | if (rc < 0) 921 | perror ("Could not set obsolete flag"); 922 | 923 | return rc; 924 | } 925 | 926 | static int flash_write (int fd_current, int fd_target, int dev_target) 927 | { 928 | int rc; 929 | 930 | switch (environment.flag_scheme) { 931 | case FLAG_NONE: 932 | break; 933 | case FLAG_INCREMENTAL: 934 | (*environment.flags)++; 935 | break; 936 | case FLAG_BOOLEAN: 937 | *environment.flags = active_flag; 938 | break; 939 | default: 940 | fprintf (stderr, "Unimplemented flash scheme %u \n", 941 | environment.flag_scheme); 942 | return -1; 943 | } 944 | 945 | #ifdef DEBUG 946 | printf ("Writing new environment at 0x%lx on %s\n", 947 | DEVOFFSET (dev_target), DEVNAME (dev_target)); 948 | #endif 949 | rc = flash_write_buf (dev_target, fd_target, environment.image, 950 | CONFIG_ENV_SIZE, DEVOFFSET (dev_target), 951 | DEVTYPE(dev_target)); 952 | if (rc < 0) 953 | return rc; 954 | 955 | if (environment.flag_scheme == FLAG_BOOLEAN) { 956 | /* Have to set obsolete flag */ 957 | off_t offset = DEVOFFSET (dev_current) + 958 | offsetof (struct env_image_redundant, flags); 959 | #ifdef DEBUG 960 | printf ("Setting obsolete flag in environment at 0x%lx on %s\n", 961 | DEVOFFSET (dev_current), DEVNAME (dev_current)); 962 | #endif 963 | flash_flag_obsolete (dev_current, fd_current, offset); 964 | } 965 | 966 | return 0; 967 | } 968 | 969 | static int flash_read (int fd) 970 | { 971 | struct mtd_info_user mtdinfo; 972 | int rc; 973 | 974 | rc = ioctl (fd, MEMGETINFO, &mtdinfo); 975 | if (rc < 0) { 976 | perror ("Cannot get MTD information"); 977 | return -1; 978 | } 979 | 980 | if (mtdinfo.type != MTD_NORFLASH && 981 | mtdinfo.type != MTD_NANDFLASH && 982 | mtdinfo.type != MTD_DATAFLASH) { 983 | fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type); 984 | return -1; 985 | } 986 | 987 | DEVTYPE(dev_current) = mtdinfo.type; 988 | 989 | rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE, 990 | DEVOFFSET (dev_current), mtdinfo.type); 991 | 992 | return (rc != CONFIG_ENV_SIZE) ? -1 : 0; 993 | } 994 | 995 | static int flash_io (int mode) 996 | { 997 | int fd_current, fd_target, rc, dev_target; 998 | 999 | /* dev_current: fd_current, erase_current */ 1000 | fd_current = open (DEVNAME (dev_current), mode); 1001 | if (fd_current < 0) { 1002 | fprintf (stderr, 1003 | "Can't open %s: %s\n", 1004 | DEVNAME (dev_current), strerror (errno)); 1005 | return -1; 1006 | } 1007 | 1008 | if (mode == O_RDWR) { 1009 | if (HaveRedundEnv) { 1010 | /* switch to next partition for writing */ 1011 | dev_target = !dev_current; 1012 | /* dev_target: fd_target, erase_target */ 1013 | fd_target = open (DEVNAME (dev_target), mode); 1014 | if (fd_target < 0) { 1015 | fprintf (stderr, 1016 | "Can't open %s: %s\n", 1017 | DEVNAME (dev_target), 1018 | strerror (errno)); 1019 | rc = -1; 1020 | goto exit; 1021 | } 1022 | } else { 1023 | dev_target = dev_current; 1024 | fd_target = fd_current; 1025 | } 1026 | 1027 | rc = flash_write (fd_current, fd_target, dev_target); 1028 | 1029 | if (HaveRedundEnv) { 1030 | if (close (fd_target)) { 1031 | fprintf (stderr, 1032 | "I/O error on %s: %s\n", 1033 | DEVNAME (dev_target), 1034 | strerror (errno)); 1035 | rc = -1; 1036 | } 1037 | } 1038 | } else { 1039 | rc = flash_read (fd_current); 1040 | } 1041 | 1042 | exit: 1043 | if (close (fd_current)) { 1044 | fprintf (stderr, 1045 | "I/O error on %s: %s\n", 1046 | DEVNAME (dev_current), strerror (errno)); 1047 | return -1; 1048 | } 1049 | 1050 | return rc; 1051 | } 1052 | 1053 | /* 1054 | * s1 is either a simple 'name', or a 'name=value' pair. 1055 | * s2 is a 'name=value' pair. 1056 | * If the names match, return the value of s2, else NULL. 1057 | */ 1058 | 1059 | static char *envmatch (char * s1, char * s2) 1060 | { 1061 | 1062 | while (*s1 == *s2++) 1063 | if (*s1++ == '=') 1064 | return s2; 1065 | if (*s1 == '\0' && *(s2 - 1) == '=') 1066 | return s2; 1067 | return NULL; 1068 | } 1069 | 1070 | /* 1071 | * Prevent confusion if running from erased flash memory 1072 | */ 1073 | int fw_env_open(void) 1074 | { 1075 | int crc0, crc0_ok; 1076 | unsigned char flag0; 1077 | void *addr0; 1078 | 1079 | int crc1, crc1_ok; 1080 | unsigned char flag1; 1081 | void *addr1; 1082 | 1083 | struct env_image_single *single; 1084 | struct env_image_redundant *redundant; 1085 | 1086 | if (parse_config ()) /* should fill envdevices */ 1087 | return -1; 1088 | 1089 | addr0 = calloc (1, CONFIG_ENV_SIZE); 1090 | if (addr0 == NULL) { 1091 | fprintf (stderr, 1092 | "Not enough memory for environment (%ld bytes)\n", 1093 | CONFIG_ENV_SIZE); 1094 | return -1; 1095 | } 1096 | 1097 | /* read environment from FLASH to local buffer */ 1098 | environment.image = addr0; 1099 | 1100 | if (HaveRedundEnv) { 1101 | redundant = addr0; 1102 | environment.crc = &redundant->crc; 1103 | environment.flags = &redundant->flags; 1104 | environment.data = redundant->data; 1105 | } else { 1106 | single = addr0; 1107 | environment.crc = &single->crc; 1108 | environment.flags = NULL; 1109 | environment.data = single->data; 1110 | } 1111 | 1112 | dev_current = 0; 1113 | if (flash_io (O_RDONLY)) 1114 | return -1; 1115 | 1116 | crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); 1117 | crc0_ok = (crc0 == *environment.crc); 1118 | if (!HaveRedundEnv) { 1119 | if (!crc0_ok) { 1120 | fprintf (stderr, 1121 | "Warning: Bad CRC, using default environment\n"); 1122 | memcpy(environment.data, default_environment, sizeof default_environment); 1123 | } 1124 | } else { 1125 | flag0 = *environment.flags; 1126 | 1127 | dev_current = 1; 1128 | addr1 = calloc (1, CONFIG_ENV_SIZE); 1129 | if (addr1 == NULL) { 1130 | fprintf (stderr, 1131 | "Not enough memory for environment (%ld bytes)\n", 1132 | CONFIG_ENV_SIZE); 1133 | return -1; 1134 | } 1135 | redundant = addr1; 1136 | 1137 | /* 1138 | * have to set environment.image for flash_read(), careful - 1139 | * other pointers in environment still point inside addr0 1140 | */ 1141 | environment.image = addr1; 1142 | if (flash_io (O_RDONLY)) 1143 | return -1; 1144 | 1145 | /* Check flag scheme compatibility */ 1146 | if (DEVTYPE(dev_current) == MTD_NORFLASH && 1147 | DEVTYPE(!dev_current) == MTD_NORFLASH) { 1148 | environment.flag_scheme = FLAG_BOOLEAN; 1149 | } else if (DEVTYPE(dev_current) == MTD_NANDFLASH && 1150 | DEVTYPE(!dev_current) == MTD_NANDFLASH) { 1151 | environment.flag_scheme = FLAG_INCREMENTAL; 1152 | } else if (DEVTYPE(dev_current) == MTD_DATAFLASH && 1153 | DEVTYPE(!dev_current) == MTD_DATAFLASH) { 1154 | environment.flag_scheme = FLAG_BOOLEAN; 1155 | } else { 1156 | fprintf (stderr, "Incompatible flash types!\n"); 1157 | return -1; 1158 | } 1159 | 1160 | crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); 1161 | crc1_ok = (crc1 == redundant->crc); 1162 | flag1 = redundant->flags; 1163 | 1164 | if (crc0_ok && !crc1_ok) { 1165 | dev_current = 0; 1166 | } else if (!crc0_ok && crc1_ok) { 1167 | dev_current = 1; 1168 | } else if (!crc0_ok && !crc1_ok) { 1169 | fprintf (stderr, 1170 | "Warning: Bad CRC, using default environment\n"); 1171 | memcpy (environment.data, default_environment, 1172 | sizeof default_environment); 1173 | dev_current = 0; 1174 | } else { 1175 | switch (environment.flag_scheme) { 1176 | case FLAG_BOOLEAN: 1177 | if (flag0 == active_flag && 1178 | flag1 == obsolete_flag) { 1179 | dev_current = 0; 1180 | } else if (flag0 == obsolete_flag && 1181 | flag1 == active_flag) { 1182 | dev_current = 1; 1183 | } else if (flag0 == flag1) { 1184 | dev_current = 0; 1185 | } else if (flag0 == 0xFF) { 1186 | dev_current = 0; 1187 | } else if (flag1 == 0xFF) { 1188 | dev_current = 1; 1189 | } else { 1190 | dev_current = 0; 1191 | } 1192 | break; 1193 | case FLAG_INCREMENTAL: 1194 | if (flag0 == 255 && flag1 == 0) 1195 | dev_current = 1; 1196 | else if ((flag1 == 255 && flag0 == 0) || 1197 | flag0 >= flag1) 1198 | dev_current = 0; 1199 | else /* flag1 > flag0 */ 1200 | dev_current = 1; 1201 | break; 1202 | default: 1203 | fprintf (stderr, "Unknown flag scheme %u \n", 1204 | environment.flag_scheme); 1205 | return -1; 1206 | } 1207 | } 1208 | 1209 | /* 1210 | * If we are reading, we don't need the flag and the CRC any 1211 | * more, if we are writing, we will re-calculate CRC and update 1212 | * flags before writing out 1213 | */ 1214 | if (dev_current) { 1215 | environment.image = addr1; 1216 | environment.crc = &redundant->crc; 1217 | environment.flags = &redundant->flags; 1218 | environment.data = redundant->data; 1219 | free (addr0); 1220 | } else { 1221 | environment.image = addr0; 1222 | /* Other pointers are already set */ 1223 | free (addr1); 1224 | } 1225 | } 1226 | return 0; 1227 | } 1228 | 1229 | 1230 | static int parse_config () 1231 | { 1232 | struct stat st; 1233 | 1234 | #if defined(CONFIG_FILE) 1235 | /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ 1236 | if (get_config (CONFIG_FILE)) { 1237 | fprintf (stderr, 1238 | "Cannot parse config file: %s\n", strerror (errno)); 1239 | return -1; 1240 | } 1241 | #else 1242 | strcpy (DEVNAME (0), DEVICE1_NAME); 1243 | DEVOFFSET (0) = DEVICE1_OFFSET; 1244 | ENVSIZE (0) = ENV1_SIZE; 1245 | /* Default values are: erase-size=env-size, #sectors=1 */ 1246 | DEVESIZE (0) = ENVSIZE (0); 1247 | ENVSECTORS (0) = 1; 1248 | #ifdef DEVICE1_ESIZE 1249 | DEVESIZE (0) = DEVICE1_ESIZE; 1250 | #endif 1251 | #ifdef DEVICE1_ENVSECTORS 1252 | ENVSECTORS (0) = DEVICE1_ENVSECTORS; 1253 | #endif 1254 | 1255 | #ifdef HAVE_REDUND 1256 | strcpy (DEVNAME (1), DEVICE2_NAME); 1257 | DEVOFFSET (1) = DEVICE2_OFFSET; 1258 | ENVSIZE (1) = ENV2_SIZE; 1259 | /* Default values are: erase-size=env-size, #sectors=1 */ 1260 | DEVESIZE (1) = ENVSIZE (1); 1261 | ENVSECTORS (1) = 1; 1262 | #ifdef DEVICE2_ESIZE 1263 | DEVESIZE (1) = DEVICE2_ESIZE; 1264 | #endif 1265 | #ifdef DEVICE2_ENVSECTORS 1266 | ENVSECTORS (1) = DEVICE2_ENVSECTORS; 1267 | #endif 1268 | HaveRedundEnv = 1; 1269 | #endif 1270 | #endif 1271 | if (stat (DEVNAME (0), &st)) { 1272 | fprintf (stderr, 1273 | "Cannot access MTD device %s: %s\n", 1274 | DEVNAME (0), strerror (errno)); 1275 | return -1; 1276 | } 1277 | 1278 | if (HaveRedundEnv && stat (DEVNAME (1), &st)) { 1279 | fprintf (stderr, 1280 | "Cannot access MTD device %s: %s\n", 1281 | DEVNAME (1), strerror (errno)); 1282 | return -1; 1283 | } 1284 | return 0; 1285 | } 1286 | 1287 | #if defined(CONFIG_FILE) 1288 | static int get_config (char *fname) 1289 | { 1290 | FILE *fp; 1291 | int i = 0; 1292 | int rc; 1293 | char dump[128]; 1294 | 1295 | fp = fopen (fname, "r"); 1296 | if (fp == NULL) 1297 | return -1; 1298 | 1299 | while (i < 2 && fgets (dump, sizeof (dump), fp)) { 1300 | /* Skip incomplete conversions and comment strings */ 1301 | if (dump[0] == '#') 1302 | continue; 1303 | 1304 | rc = sscanf (dump, "%s %lx %lx %lx %lx", 1305 | DEVNAME (i), 1306 | &DEVOFFSET (i), 1307 | &ENVSIZE (i), 1308 | &DEVESIZE (i), 1309 | &ENVSECTORS (i)); 1310 | 1311 | if (rc < 3) 1312 | continue; 1313 | 1314 | if (rc < 4) 1315 | /* Assume the erase size is the same as the env-size */ 1316 | DEVESIZE(i) = ENVSIZE(i); 1317 | 1318 | if (rc < 5) 1319 | /* Default - 1 sector */ 1320 | ENVSECTORS (i) = 1; 1321 | 1322 | i++; 1323 | } 1324 | fclose (fp); 1325 | 1326 | HaveRedundEnv = i - 1; 1327 | if (!i) { /* No valid entries found */ 1328 | errno = EINVAL; 1329 | return -1; 1330 | } else 1331 | return 0; 1332 | } 1333 | #endif 1334 | -------------------------------------------------------------------------------- /fwupgrade-uboot-env.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2002-2008 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 | * 5 | * See file CREDITS for list of people who contributed to this 6 | * project. 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation; either version 2 of 11 | * the License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 | * MA 02111-1307 USA 22 | */ 23 | 24 | #define CONFIG_FILE "/etc/fw_env.config" 25 | 26 | extern int fw_printenv(int argc, char *argv[]); 27 | extern char *fw_getenv (char *name); 28 | extern int fw_setenv (int argc, char *argv[]); 29 | extern int fw_parse_script(char *fname); 30 | extern int fw_env_open(void); 31 | extern int fw_env_write(char *name, char *value); 32 | extern char *fw_env_read(char *name); 33 | extern int fw_env_close(void); 34 | 35 | extern unsigned long crc32 (unsigned long, const unsigned char *, unsigned); 36 | -------------------------------------------------------------------------------- /fwupgrade.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE /* for basename */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "fwupgrade.h" 12 | #include "fwupgrade-cgi.h" 13 | #include "fwupgrade-file.h" 14 | #include "fwupgrade-uboot-env.h" 15 | 16 | #define THIS_HWID 0x2424 17 | 18 | struct fwupgrade_action { 19 | const char *part_name; 20 | const char *uboot_part1; 21 | const char *uboot_part2; 22 | const char *kernel_part1; 23 | const char *kernel_part2; 24 | enum { TYPE_MTD, TYPE_UBI } type; 25 | }; 26 | 27 | struct fwupgrade_action actions[FWPART_COUNT]; 28 | 29 | int flash_fwpart(const char *part, const char *data, unsigned int len, 30 | int type) 31 | { 32 | char cmd[1024]; 33 | int ret; 34 | FILE *flash_pipe; 35 | size_t sz; 36 | 37 | if (type == TYPE_MTD) { 38 | printf("Erasing partition %s\n", part); 39 | 40 | snprintf(cmd, sizeof(cmd), "flash_erase -q /dev/%s 0 0", part); 41 | ret = system(cmd); 42 | if (ret) { 43 | printf("ERROR: Unable to erase partition %s, aborting.\n", part); 44 | return -1; 45 | } 46 | 47 | printf("Flashing partition %s\n", part); 48 | 49 | snprintf(cmd, sizeof(cmd), "nandwrite -q -p /dev/%s -", part); 50 | } else { 51 | printf("Flashing partition %s\n", part); 52 | 53 | snprintf(cmd, sizeof(cmd), "ubiupdatevol /dev/ubi/%s --size=%d -", 54 | part, len); 55 | } 56 | 57 | flash_pipe = popen(cmd, "w"); 58 | if (! flash_pipe) { 59 | printf("ERROR: Unable to flash partition %s, aborting\n", part); 60 | return -1; 61 | } 62 | 63 | sz = fwrite(data, len, 1, flash_pipe); 64 | if (sz != 1) { 65 | printf("ERROR: Unable to flash partition %s, aborting\n", part); 66 | return -1; 67 | } 68 | 69 | ret = pclose(flash_pipe); 70 | if (! WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { 71 | printf("ERROR: Unable to flash partition %s, aborting\n", part); 72 | return -1; 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | int handle_fwpart(const char *partname, const char *data, unsigned int len) 79 | { 80 | struct fwupgrade_action *act = NULL; 81 | const char *current_part, *next_kernel_part, *next_uboot_part; 82 | char uboot_varname[64]; 83 | int i, ret; 84 | 85 | for (i = 0; i < FWPART_COUNT; i++) { 86 | if (actions[i].part_name == NULL) 87 | break; 88 | 89 | if (! strcmp(actions[i].part_name, partname)) { 90 | act = & actions[i]; 91 | break; 92 | } 93 | } 94 | 95 | if (! act) { 96 | printf("ERROR: Unknown partition '%s' in firmware image, aborting.\n", partname); 97 | return -1; 98 | } 99 | 100 | /* The u-boot variable is different according to MTD/UBI */ 101 | if (act->type == TYPE_UBI) { 102 | snprintf(uboot_varname, sizeof(uboot_varname), "%s_ubivol", 103 | partname); 104 | } else { 105 | snprintf(uboot_varname, sizeof(uboot_varname), "%s_mtdpart", 106 | partname); 107 | } 108 | 109 | current_part = fw_env_read(uboot_varname); 110 | if (! current_part) { 111 | printf("ERROR: Cannot find current partition for '%s', aborting.\n", 112 | partname); 113 | return -1; 114 | } 115 | 116 | if (! strcmp(current_part, act->uboot_part1)) { 117 | next_kernel_part = act->kernel_part2; 118 | next_uboot_part = act->uboot_part2; 119 | } 120 | else if (! strcmp(current_part, act->uboot_part2)) { 121 | next_kernel_part = act->kernel_part1; 122 | next_uboot_part = act->uboot_part1; 123 | } 124 | else { 125 | printf("ERROR: Invalid current partition '%s' for %s, aborting.\n", 126 | current_part, act->part_name); 127 | return -1; 128 | } 129 | 130 | ret = flash_fwpart(next_kernel_part, data, len, act->type); 131 | if (ret) 132 | return ret; 133 | 134 | fw_env_write(uboot_varname, (char*) next_uboot_part); 135 | 136 | return 0; 137 | } 138 | 139 | int apply_upgrade(const char *data, unsigned int data_length) 140 | { 141 | int i, ret; 142 | struct fwheader *header = (struct fwheader *) data; 143 | 144 | if (le32toh(header->magic) != FWUPGRADE_MAGIC) { 145 | printf("ERROR: Invalid firmware magic, aborting.\n"); 146 | return -1; 147 | } 148 | 149 | if (le32toh(header->hwid) != THIS_HWID) { 150 | printf("ERROR: Invalid HWID, aborting.\n"); 151 | return -1; 152 | } 153 | 154 | /* First loop to verify the CRC */ 155 | for (i = 0; i < FWPART_COUNT; i++) { 156 | unsigned int sz, offset; 157 | char computed_crc[FWPART_CRC_SZ]; 158 | 159 | sz = le32toh(header->parts[i].length); 160 | if (! sz) 161 | continue; 162 | 163 | printf("Checking part %s\n", header->parts[i].name); 164 | 165 | offset = le32toh(header->parts[i].offset); 166 | 167 | md5(data + offset, sz, computed_crc); 168 | if (memcmp(computed_crc, header->parts[i].crc, FWPART_CRC_SZ)) { 169 | printf("ERROR: Invalid CRC in firmware image part %s\n", 170 | header->parts[i].name); 171 | return -1; 172 | } 173 | } 174 | 175 | ret = fw_env_open(); 176 | if (ret) { 177 | printf("ERROR: Cannot read the U-Boot environment, aborting.\n"); 178 | return -1; 179 | } 180 | 181 | /* Second loop to actually apply the upgrade */ 182 | for (i = 0; i < FWPART_COUNT; i++) { 183 | unsigned int sz, offset; 184 | int ret; 185 | 186 | sz = le32toh(header->parts[i].length); 187 | if (! sz) 188 | continue; 189 | 190 | printf("Applying part %s\n", header->parts[i].name); 191 | 192 | offset = le32toh(header->parts[i].offset); 193 | 194 | ret = handle_fwpart(header->parts[i].name, data + offset, sz); 195 | if (ret) 196 | return ret; 197 | } 198 | 199 | ret = fw_env_close(); 200 | if (ret) { 201 | printf("ERROR: Could not rewrite U-Boot environment, aborting\n"); 202 | return -1; 203 | } 204 | 205 | return 0; 206 | } 207 | 208 | int parse_configuration(void) 209 | { 210 | char line[255]; 211 | int action = 0; 212 | 213 | FILE *cfg = fopen("/etc/fwupgrade.conf", "r"); 214 | if (! cfg) 215 | return -1; 216 | 217 | memset(actions, 0, sizeof(actions)); 218 | 219 | while (fgets(line, sizeof(line), cfg)) { 220 | char *tmp, *cur; 221 | enum { FIELD_PART_NAME, 222 | FIELD_UBOOT_PART1, 223 | FIELD_UBOOT_PART2, 224 | FIELD_KERNEL_PART1, 225 | FIELD_KERNEL_PART2, 226 | FIELD_TYPE} field = FIELD_PART_NAME; 227 | 228 | if (action >= FWPART_COUNT) { 229 | fclose(cfg); 230 | return -1; 231 | } 232 | 233 | /* In case the partition type is not defined, assume 234 | * MTD by default, for backward compatibility */ 235 | actions[action].type = TYPE_MTD; 236 | 237 | /* Remove ending newline if any */ 238 | if (line[strlen(line)-1] == '\n') 239 | line[strlen(line)-1] = '\0'; 240 | 241 | /* Split the four ':' separated fields */ 242 | tmp = line; 243 | while((cur = strtok(tmp, ":")) != NULL) { 244 | if (field == FIELD_PART_NAME) 245 | actions[action].part_name = strdup(cur); 246 | else if (field == FIELD_UBOOT_PART1) 247 | actions[action].uboot_part1 = strdup(cur); 248 | else if (field == FIELD_UBOOT_PART2) 249 | actions[action].uboot_part2 = strdup(cur); 250 | else if (field == FIELD_KERNEL_PART1) 251 | actions[action].kernel_part1 = strdup(cur); 252 | else if (field == FIELD_KERNEL_PART2) 253 | actions[action].kernel_part2 = strdup(cur); 254 | else if (field == FIELD_TYPE) { 255 | if (!strcmp(cur, "ubi")) 256 | actions[action].type = TYPE_UBI; 257 | else 258 | actions[action].type = TYPE_MTD; 259 | } 260 | field++; 261 | tmp = NULL; 262 | } 263 | 264 | action++; 265 | } 266 | 267 | fclose(cfg); 268 | 269 | return 0; 270 | } 271 | 272 | int main(int argc, char *argv[]) 273 | { 274 | char *data; 275 | unsigned int data_length; 276 | int ret; 277 | char *execname = basename(argv[0]); 278 | int ascgi; 279 | 280 | if (! execname) { 281 | fprintf(stderr, "No executable name\n"); 282 | return -1; 283 | } 284 | 285 | if (! strcmp(execname, "fwupgrade")) 286 | ascgi = 0; 287 | else if (! strcmp(execname, "fwupgrade-cgi")) 288 | ascgi = 1; 289 | else { 290 | fprintf(stderr, "Unknown executable name %s\n", execname); 291 | return -1; 292 | } 293 | 294 | /* Switch to line-oriented buffering for stdout so that 295 | * messages are sent to the HTTP client right away */ 296 | setvbuf(stdout, NULL, _IOLBF, BUFSIZ); 297 | 298 | if (ascgi) 299 | printf("Content-type: text/plain\n\n"); 300 | 301 | ret = parse_configuration(); 302 | if (ret < 0) { 303 | fprintf(stderr, "Problem parsing configuration\n"); 304 | return -1; 305 | } 306 | 307 | if (ascgi) { 308 | data = fwupgrade_cgi_receive_data(& data_length); 309 | if (! data) { 310 | printf("Failed to receive data\n"); 311 | return -1; 312 | } 313 | } else { 314 | data = fwupgrade_load_file_data(argv[1], & data_length); 315 | if (! data) { 316 | fprintf(stderr, "Failed to load data\n"); 317 | return -1; 318 | } 319 | } 320 | 321 | ret = apply_upgrade(data, data_length); 322 | if (ret) { 323 | printf("The system upgrade failed\n"); 324 | if (ascgi) 325 | close(STDOUT_FILENO); 326 | return -1; 327 | } else { 328 | printf("The system upgrade completed successfully\n"); 329 | if (ascgi) { 330 | fflush(stdout); 331 | close(STDOUT_FILENO); 332 | } 333 | sync(); 334 | sleep(1); 335 | reboot(LINUX_REBOOT_CMD_RESTART); 336 | } 337 | 338 | return 0; 339 | } 340 | -------------------------------------------------------------------------------- /fwupgrade.conf: -------------------------------------------------------------------------------- 1 | kernel:mtd0:mtd1 2 | rootfs:mtd2:mtd3 -------------------------------------------------------------------------------- /fwupgrade.h: -------------------------------------------------------------------------------- 1 | #ifndef FWUPGRADE_H 2 | #define FWUPGRADE_H 3 | 4 | #include 5 | 6 | #define FWPART_NAME_SZ 16 7 | #define FWPART_CRC_SZ 16 8 | 9 | /* Structure describing one part of the firmware. */ 10 | struct fwpart { 11 | /* 0-terminated string */ 12 | char name[FWPART_NAME_SZ]; 13 | 14 | /* MD5SUM of the part data */ 15 | char crc[FWPART_CRC_SZ]; 16 | 17 | /* Size of the part, in bytes */ 18 | unsigned int length; 19 | 20 | /* Offset of the part, in bytes, from the beginning of the 21 | file */ 22 | unsigned int offset; 23 | 24 | /* Pad the structure so that it takes 128 bytes. This should 25 | allows future extensions */ 26 | char unused[88]; 27 | }; 28 | 29 | #define FWPART_COUNT 8 30 | 31 | #define FWUPGRADE_MAGIC 0x5E7F28CD 32 | 33 | /* Header of the firmware. We pad it so that the structure takes 2048 34 | bytes, for future extensions */ 35 | struct fwheader { 36 | unsigned int magic; 37 | unsigned int hwid; 38 | unsigned int flags; 39 | struct fwpart parts[FWPART_COUNT]; 40 | char unused[1012]; 41 | }; 42 | 43 | void md5 (const char *input, int len, char output[16]); 44 | 45 | #endif /* FWUPGRADE_H */ 46 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Firmware upgrade

4 |
5 | 6 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was transplanted with slight modifications from Linux sources 3 | * (fs/cifs/md5.c) into U-Boot by Bartlomiej Sieka . 4 | */ 5 | 6 | /* 7 | * This code implements the MD5 message-digest algorithm. 8 | * The algorithm is due to Ron Rivest. This code was 9 | * written by Colin Plumb in 1993, no copyright is claimed. 10 | * This code is in the public domain; do with it what you wish. 11 | * 12 | * Equivalent code is available from RSA Data Security, Inc. 13 | * This code has been tested against that, and is equivalent, 14 | * except that you don't need to include two pages of legalese 15 | * with every copy. 16 | * 17 | * To compute the message digest of a chunk of bytes, declare an 18 | * MD5Context structure, pass it to MD5Init, call MD5Update as 19 | * needed on buffers full of bytes, and then call MD5Final, which 20 | * will fill a supplied 16-byte array with the digest. 21 | */ 22 | 23 | /* This code slightly modified to fit into Samba by 24 | abartlet@samba.org Jun 2001 25 | and to fit the cifs vfs by 26 | Steve French sfrench@us.ibm.com */ 27 | 28 | #include 29 | #include 30 | 31 | struct MD5Context { 32 | uint32_t buf[4]; 33 | uint32_t bits[2]; 34 | unsigned char in[64]; 35 | }; 36 | 37 | static void 38 | MD5Transform(uint32_t buf[4], uint32_t const in[16]); 39 | 40 | /* 41 | * Note: this code is harmless on little-endian machines. 42 | */ 43 | static void 44 | byteReverse(unsigned char *buf, unsigned longs) 45 | { 46 | uint32_t t; 47 | do { 48 | t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 49 | ((unsigned) buf[1] << 8 | buf[0]); 50 | *(uint32_t *) buf = t; 51 | buf += 4; 52 | } while (--longs); 53 | } 54 | 55 | /* 56 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 57 | * initialization constants. 58 | */ 59 | static void 60 | MD5Init(struct MD5Context *ctx) 61 | { 62 | ctx->buf[0] = 0x67452301; 63 | ctx->buf[1] = 0xefcdab89; 64 | ctx->buf[2] = 0x98badcfe; 65 | ctx->buf[3] = 0x10325476; 66 | 67 | ctx->bits[0] = 0; 68 | ctx->bits[1] = 0; 69 | } 70 | 71 | /* 72 | * Update context to reflect the concatenation of another buffer full 73 | * of bytes. 74 | */ 75 | static void 76 | MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) 77 | { 78 | register uint32_t t; 79 | 80 | /* Update bitcount */ 81 | 82 | t = ctx->bits[0]; 83 | if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) 84 | ctx->bits[1]++; /* Carry from low to high */ 85 | ctx->bits[1] += len >> 29; 86 | 87 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ 88 | 89 | /* Handle any leading odd-sized chunks */ 90 | 91 | if (t) { 92 | unsigned char *p = (unsigned char *) ctx->in + t; 93 | 94 | t = 64 - t; 95 | if (len < t) { 96 | memmove(p, buf, len); 97 | return; 98 | } 99 | memmove(p, buf, t); 100 | byteReverse(ctx->in, 16); 101 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 102 | buf += t; 103 | len -= t; 104 | } 105 | /* Process data in 64-byte chunks */ 106 | 107 | while (len >= 64) { 108 | memmove(ctx->in, buf, 64); 109 | byteReverse(ctx->in, 16); 110 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 111 | buf += 64; 112 | len -= 64; 113 | } 114 | 115 | /* Handle any remaining bytes of data. */ 116 | 117 | memmove(ctx->in, buf, len); 118 | } 119 | 120 | /* 121 | * Final wrapup - pad to 64-byte boundary with the bit pattern 122 | * 1 0* (64-bit count of bits processed, MSB-first) 123 | */ 124 | static void 125 | MD5Final(unsigned char digest[16], struct MD5Context *ctx) 126 | { 127 | unsigned int count; 128 | unsigned char *p; 129 | 130 | /* Compute number of bytes mod 64 */ 131 | count = (ctx->bits[0] >> 3) & 0x3F; 132 | 133 | /* Set the first char of padding to 0x80. This is safe since there is 134 | always at least one byte free */ 135 | p = ctx->in + count; 136 | *p++ = 0x80; 137 | 138 | /* Bytes of padding needed to make 64 bytes */ 139 | count = 64 - 1 - count; 140 | 141 | /* Pad out to 56 mod 64 */ 142 | if (count < 8) { 143 | /* Two lots of padding: Pad the first block to 64 bytes */ 144 | memset(p, 0, count); 145 | byteReverse(ctx->in, 16); 146 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 147 | 148 | /* Now fill the next block with 56 bytes */ 149 | memset(ctx->in, 0, 56); 150 | } else { 151 | /* Pad block to 56 bytes */ 152 | memset(p, 0, count - 8); 153 | } 154 | byteReverse(ctx->in, 14); 155 | 156 | /* Append length in bits and transform */ 157 | ((uint32_t *) ctx->in)[14] = ctx->bits[0]; 158 | ((uint32_t *) ctx->in)[15] = ctx->bits[1]; 159 | 160 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); 161 | byteReverse((unsigned char *) ctx->buf, 4); 162 | memmove(digest, ctx->buf, 16); 163 | memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ 164 | } 165 | 166 | /* The four core functions - F1 is optimized somewhat */ 167 | 168 | /* #define F1(x, y, z) (x & y | ~x & z) */ 169 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 170 | #define F2(x, y, z) F1(z, x, y) 171 | #define F3(x, y, z) (x ^ y ^ z) 172 | #define F4(x, y, z) (y ^ (x | ~z)) 173 | 174 | /* This is the central step in the MD5 algorithm. */ 175 | #define MD5STEP(f, w, x, y, z, data, s) \ 176 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 177 | 178 | /* 179 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 180 | * reflect the addition of 16 longwords of new data. MD5Update blocks 181 | * the data and converts bytes into longwords for this routine. 182 | */ 183 | static void 184 | MD5Transform(uint32_t buf[4], uint32_t const in[16]) 185 | { 186 | register uint32_t a, b, c, d; 187 | 188 | a = buf[0]; 189 | b = buf[1]; 190 | c = buf[2]; 191 | d = buf[3]; 192 | 193 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); 194 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); 195 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); 196 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); 197 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); 198 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); 199 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); 200 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); 201 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); 202 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); 203 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 204 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 205 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 206 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 207 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 208 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 209 | 210 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); 211 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); 212 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 213 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); 214 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); 215 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 216 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 217 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); 218 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); 219 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 220 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); 221 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); 222 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 223 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); 224 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); 225 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 226 | 227 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); 228 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); 229 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 230 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 231 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); 232 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); 233 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); 234 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 235 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 236 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); 237 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); 238 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); 239 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); 240 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 241 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 242 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); 243 | 244 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); 245 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); 246 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 247 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); 248 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 249 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); 250 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 251 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); 252 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); 253 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 254 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); 255 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 256 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); 257 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 258 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); 259 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); 260 | 261 | buf[0] += a; 262 | buf[1] += b; 263 | buf[2] += c; 264 | buf[3] += d; 265 | } 266 | 267 | /* 268 | * Calculate and store in 'output' the MD5 digest of 'len' bytes at 269 | * 'input'. 'output' must have enough space to hold 16 bytes. 270 | */ 271 | void 272 | md5 (const char *input, int len, char output[16]) 273 | { 274 | struct MD5Context context; 275 | 276 | MD5Init(&context); 277 | MD5Update(&context, (const unsigned char *) input, len); 278 | MD5Final((unsigned char *)output, &context); 279 | } 280 | --------------------------------------------------------------------------------