1 //-----------------------------------------------------------------------------
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // Compression tool for FPGA config files. Compress several *.bit files at
9 // compile time. Decompression is done at run time (see fpgaloader.c).
10 // This uses the lz4 library tuned to this specific case. The small file sizes
11 // allow to use "insane" parameters for optimum compression ratio.
12 //-----------------------------------------------------------------------------
25 #define MIN(a,b) ((a) < (b) ? (a) : (b))
28 static void usage(void) {
29 fprintf(stdout
, "Usage: fpga_compress <infile1> <infile2> ... <infile_n> <outfile>\n");
30 fprintf(stdout
, " Combine n FPGA bitstream files and compress them into one.\n\n");
31 fprintf(stdout
, " fpga_compress -v <infile1> <infile2> ... <infile_n> <outfile>\n");
32 fprintf(stdout
, " Extract Version Information from FPGA bitstream files and write it to <outfile>\n\n");
33 fprintf(stdout
, " fpga_compress -d <infile> <outfile(s)>\n");
34 fprintf(stdout
, " Decompress <infile>. Write result to <outfile(s)>\n\n");
37 static bool all_feof(FILE *infile
[], uint8_t num_infiles
) {
38 for (uint16_t i
= 0; i
< num_infiles
; i
++) {
39 if (!feof(infile
[i
])) {
46 static int zlib_compress(FILE *infile
[], uint8_t num_infiles
, FILE *outfile
) {
48 uint8_t *fpga_config
= calloc(num_infiles
* FPGA_CONFIG_SIZE
, sizeof(uint8_t));
49 if (fpga_config
== NULL
) {
50 fprintf(stderr
, "failed to allocate memory");
51 return (EXIT_FAILURE
);
54 // read the input files. Interleave them into fpga_config[]
55 uint32_t total_size
= 0;
58 if (total_size
> num_infiles
* FPGA_CONFIG_SIZE
) {
60 "Input files too big (total > %li bytes). These are probably not PM3 FPGA config files.\n"
61 , num_infiles
* FPGA_CONFIG_SIZE
65 return (EXIT_FAILURE
);
68 for (uint16_t j
= 0; j
< num_infiles
; j
++) {
69 for (uint16_t k
= 0; k
< FPGA_INTERLEAVE_SIZE
; k
++) {
70 uint8_t c
= (uint8_t)fgetc(infile
[j
]);
72 if (!feof(infile
[j
])) {
73 fpga_config
[total_size
++] = c
;
74 } else if (num_infiles
> 1) {
75 fpga_config
[total_size
++] = '\0';
80 } while (all_feof(infile
, num_infiles
) == false);
82 uint32_t buffer_size
= FPGA_RING_BUFFER_BYTES
;
84 if (num_infiles
== 1) {
86 buffer_size
= 1024 * 1024;
89 uint32_t outsize_max
= LZ4_compressBound(buffer_size
);
91 char *outbuf
= calloc(outsize_max
, sizeof(char));
93 fprintf(stderr
, "failed to allocate memory");
95 return (EXIT_FAILURE
);
98 char *ring_buffer
= calloc(buffer_size
, sizeof(char));
99 if (ring_buffer
== NULL
) {
100 fprintf(stderr
, "failed to allocate memory");
103 return (EXIT_FAILURE
);
106 LZ4_streamHC_t
*lz4_streamhc
= LZ4_createStreamHC();
107 LZ4_resetStreamHC_fast(lz4_streamhc
, LZ4HC_CLEVEL_MAX
);
112 while (current_in
< total_size
) {
114 int bytes_to_copy
= MIN(FPGA_RING_BUFFER_BYTES
, (total_size
- current_in
));
116 memcpy(ring_buffer
, fpga_config
+ current_in
, bytes_to_copy
);
118 int cmp_bytes
= LZ4_compress_HC_continue(lz4_streamhc
, ring_buffer
, outbuf
, bytes_to_copy
, outsize_max
);
120 fprintf(stderr
, "(lz4 - zlib_compress) error, got negative number of bytes from LZ4_compress_HC_continue call. got %d", cmp_bytes
);
124 LZ4_freeStreamHC(lz4_streamhc
);
125 return (EXIT_FAILURE
);
129 fwrite(&cmp_bytes
, sizeof(int), 1, outfile
);
131 // write compressed data
132 fwrite(outbuf
, sizeof(char), cmp_bytes
, outfile
);
134 current_in
+= bytes_to_copy
;
135 current_out
+= cmp_bytes
;
138 // free allocated buffers
142 LZ4_freeStreamHC(lz4_streamhc
);
144 if (current_out
== 0) {
145 fprintf(stderr
, "error in lz4");
146 return (EXIT_FAILURE
);
148 fprintf(stdout
, "compressed %u input bytes to %d output bytes\n", total_size
, current_out
);
150 return (EXIT_SUCCESS
);
153 typedef struct lz4_stream_s
{
154 LZ4_streamDecode_t
*lz4StreamDecode
;
160 // Call it either with opened infile + outsize=0
161 // or with opened infile, opened outfiles, num_outfiles and valid outsize
162 static int zlib_decompress(FILE *infile
, FILE *outfiles
[], uint8_t num_outfiles
, long *outsize
) {
164 if (num_outfiles
> 10) {
165 return (EXIT_FAILURE
);
168 LZ4_streamDecode_t lz4StreamDecode_body
= {{ 0 }};
169 char outbuf
[FPGA_RING_BUFFER_BYTES
] = {0};
172 fseek(infile
, 0L, SEEK_END
);
173 long infile_size
= ftell(infile
);
174 fseek(infile
, 0L, SEEK_SET
);
176 if (infile_size
<= 0) {
177 printf("error, when getting filesize");
178 return (EXIT_FAILURE
);
181 char *outbufall
= NULL
;
183 outbufall
= calloc(*outsize
, sizeof(char));
184 if (outbufall
== NULL
) {
185 return (EXIT_FAILURE
);
189 char *inbuf
= calloc(infile_size
, sizeof(char));
194 return (EXIT_FAILURE
);
197 size_t num_read
= fread(inbuf
, sizeof(char), infile_size
, infile
);
199 if (num_read
!= infile_size
) {
204 return (EXIT_FAILURE
);
207 lz4_stream compressed_fpga_stream
;
208 // initialize lz4 structures
209 compressed_fpga_stream
.lz4StreamDecode
= &lz4StreamDecode_body
;
210 compressed_fpga_stream
.next_in
= inbuf
;
211 compressed_fpga_stream
.avail_in
= infile_size
;
214 while (compressed_fpga_stream
.avail_in
> 0) {
216 memcpy(&cmp_bytes
, compressed_fpga_stream
.next_in
, sizeof(int));
217 compressed_fpga_stream
.next_in
+= 4;
218 compressed_fpga_stream
.avail_in
-= cmp_bytes
+ 4;
220 const int decBytes
= LZ4_decompress_safe_continue(compressed_fpga_stream
.lz4StreamDecode
, compressed_fpga_stream
.next_in
, outbuf
, cmp_bytes
, FPGA_RING_BUFFER_BYTES
);
225 if (outbufall
!= NULL
) {
226 memcpy(outbufall
+ total_size
, outbuf
, decBytes
);
229 total_size
+= decBytes
;
230 compressed_fpga_stream
.next_in
+= cmp_bytes
;
233 if (outbufall
== NULL
) {
234 *outsize
= total_size
;
235 fseek(infile
, 0L, SEEK_SET
);
239 // seeking for trailing zeroes
241 long outfilesizes
[10] = {0};
242 for (long k
= 0; k
< *outsize
/ (FPGA_INTERLEAVE_SIZE
* num_outfiles
); k
++) {
243 for (uint16_t j
= 0; j
< num_outfiles
; j
++) {
244 for (long i
= 0; i
< FPGA_INTERLEAVE_SIZE
; i
++) {
245 if (outbufall
[offset
+ i
]) {
246 outfilesizes
[j
] = (k
* FPGA_INTERLEAVE_SIZE
) + i
+ 1;
249 offset
+= FPGA_INTERLEAVE_SIZE
;
254 // FPGA bit file ends with 16 zeroes
255 for (uint16_t j
= 0; j
< num_outfiles
; j
++) {
256 outfilesizes
[j
] += 16;
257 total_size
+= outfilesizes
[j
];
261 for (long k
= 0; k
< *outsize
/ (FPGA_INTERLEAVE_SIZE
* num_outfiles
); k
++) {
262 for (uint16_t j
= 0; j
< num_outfiles
; j
++) {
263 if (k
* FPGA_INTERLEAVE_SIZE
< outfilesizes
[j
]) {
264 uint16_t chunk
= (outfilesizes
[j
] - (k
* FPGA_INTERLEAVE_SIZE
) < FPGA_INTERLEAVE_SIZE
) ?
265 outfilesizes
[j
] - (k
* FPGA_INTERLEAVE_SIZE
) : FPGA_INTERLEAVE_SIZE
;
267 fwrite(outbufall
+ offset
, chunk
, sizeof(char), outfiles
[j
]);
269 offset
+= FPGA_INTERLEAVE_SIZE
;
272 printf("uncompressed %li input bytes to %li output bytes\n", infile_size
, total_size
);
277 return (EXIT_SUCCESS
);
281 /* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence
282 * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01
283 * After that the format is 1 byte section type (ASCII character), 2 byte length
284 * (big endian), <length> bytes content. Except for section 'e' which has 4 bytes
287 static int bitparse_find_section(FILE *infile
, char section_name
, unsigned int *section_length
) {
289 #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section
292 uint16_t numbytes
= 0;
293 while (numbytes
< MAX_FPGA_BIT_STREAM_HEADER_SEARCH
) {
294 char current_name
= (char)fgetc(infile
);
296 if (current_name
< 'a' || current_name
> 'e') {
297 /* Strange section name, abort */
300 uint32_t current_length
= 0;
302 switch (current_name
) {
304 /* Four byte length field */
305 for (int i
= 0; i
< 4; i
++) {
307 /* image length sanity check, should be under 300KB */
308 if ((tmp
< 0) || (tmp
> 300 * 1024)) {
311 current_length
+= tmp
<< (24 - (i
* 8));
315 default: /* Fall through, two byte length field */
316 for (int i
= 0; i
< 2; i
++) {
318 /* if name, date or time fields are too long, we probably shouldn't parse them */
319 if ((tmp
< 0) || (tmp
> 64)) {
322 current_length
+= tmp
<< (8 - (i
* 8));
328 if (current_name
!= 'e' && current_length
> 255) {
329 /* Maybe a parse error */
333 if (current_name
== section_name
) {
335 *section_length
= current_length
;
340 for (uint32_t i
= 0; i
< current_length
&& numbytes
< MAX_FPGA_BIT_STREAM_HEADER_SEARCH
; i
++) {
348 static int FpgaGatherVersion(FILE *infile
, char *infile_name
, char *dst
, int len
) {
349 uint32_t fpga_info_len
;
350 char tempstr
[40] = {0x00};
354 for (uint16_t i
= 0; i
< FPGA_BITSTREAM_FIXED_HEADER_SIZE
; i
++) {
355 if (fgetc(infile
) != bitparse_fixed_header
[i
]) {
356 fprintf(stderr
, "Invalid FPGA file. Aborting...\n\n");
357 return (EXIT_FAILURE
);
361 if (bitparse_find_section(infile
, 'a', &fpga_info_len
)) {
362 for (uint32_t i
= 0; i
< fpga_info_len
; i
++) {
363 char c
= (char)fgetc(infile
);
364 if (i
< sizeof(tempstr
)) {
369 strncat(dst
, tempstr
, len
- strlen(dst
) - 1);
372 strncat(dst
, " image ", len
- strlen(dst
) - 1);
373 if (bitparse_find_section(infile
, 'b', &fpga_info_len
)) {
374 for (uint32_t i
= 0; i
< fpga_info_len
; i
++) {
375 char c
= (char)fgetc(infile
);
376 if (i
< sizeof(tempstr
)) {
380 strncat(dst
, tempstr
, len
- strlen(dst
) - 1);
383 strncat(dst
, " ", len
- strlen(dst
) - 1);
384 if (bitparse_find_section(infile
, 'c', &fpga_info_len
)) {
385 for (uint32_t i
= 0; i
< fpga_info_len
; i
++) {
386 char c
= (char)fgetc(infile
);
387 if (i
< sizeof(tempstr
)) {
388 if (c
== '/') c
= '-';
389 if (c
== ' ') c
= '0';
393 strncat(dst
, tempstr
, len
- strlen(dst
) - 1);
396 if (bitparse_find_section(infile
, 'd', &fpga_info_len
)) {
397 strncat(dst
, " ", len
- strlen(dst
) - 1);
398 for (uint32_t i
= 0; i
< fpga_info_len
; i
++) {
399 char c
= (char)fgetc(infile
);
400 if (i
< sizeof(tempstr
)) {
401 if (c
== ' ') c
= '0';
405 strncat(dst
, tempstr
, len
- strlen(dst
) - 1);
410 static void print_version_info_preamble(FILE *outfile
, int num_infiles
) {
411 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
412 fprintf(outfile
, "// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.\n");
413 fprintf(outfile
, "//\n");
414 fprintf(outfile
, "// This code is licensed to you under the terms of the GNU GPL, version 3 or,\n");
415 fprintf(outfile
, "// at your option, any later version. See the LICENSE.txt file for the text of\n");
416 fprintf(outfile
, "// the license.\n");
417 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
418 fprintf(outfile
, "// Version information on fpga images\n");
419 fprintf(outfile
, "//\n");
420 fprintf(outfile
, "// This file is generated by fpga_compress. Don't edit!\n");
421 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
422 fprintf(outfile
, "\n\n");
423 fprintf(outfile
, "#include \"fpga.h\"\n\n");
424 fprintf(outfile
, "const int g_fpga_bitstream_num = %d;\n", num_infiles
);
425 fprintf(outfile
, "const FPGA_VERSION_INFORMATION g_fpga_version_information[%d] = {\n", num_infiles
);
428 static int generate_fpga_version_info(FILE *infile
[], char *infile_names
[], int num_infiles
, FILE *outfile
) {
430 char version_string
[80] = "";
432 print_version_info_preamble(outfile
, num_infiles
);
434 for (int i
= 0; i
< num_infiles
; i
++) {
435 FpgaGatherVersion(infile
[i
], infile_names
[i
], version_string
, sizeof(version_string
));
436 fprintf(outfile
, " { \"%s\"", version_string
);
438 if (!memcmp("fpga_pm3_lf.ncd", version_string
, sizeof("fpga_pm3_lf.ncd") - 1))
439 fprintf(outfile
, ", FPGA_BITSTREAM_LF }");
440 else if (!memcmp("fpga_pm3_hf_15.ncd", version_string
, sizeof("fpga_pm3_hf_15.ncd") - 1))
441 fprintf(outfile
, ", FPGA_BITSTREAM_HF_15 }");
442 else if (!memcmp("fpga_pm3_hf.ncd", version_string
, sizeof("fpga_pm3_hf.ncd") - 1))
443 fprintf(outfile
, ", FPGA_BITSTREAM_HF }");
444 else if (!memcmp("fpga_pm3_felica.ncd", version_string
, sizeof("fpga_pm3_felica.ncd") - 1))
445 fprintf(outfile
, ", FPGA_BITSTREAM_HF_FELICA }");
447 fprintf(outfile
, ", FPGA_BITSTREAM_UNKNOWN }");
449 if (i
!= num_infiles
- 1) {
450 fprintf(outfile
, ",");
452 fprintf(outfile
, "\n");
454 fprintf(outfile
, "};\n");
458 int main(int argc
, char **argv
) {
459 if (argc
== 1 || argc
== 2) {
461 return (EXIT_FAILURE
);
464 if (!strcmp(argv
[1], "-d")) { // Decompress
468 return (EXIT_FAILURE
);
471 uint8_t num_output_files
= argc
- 3;
472 FILE **outfiles
= calloc(num_output_files
, sizeof(FILE *));
473 char **outfile_names
= calloc(num_output_files
, sizeof(char *));
474 for (uint8_t i
= 0; i
< num_output_files
; i
++) {
475 outfile_names
[i
] = argv
[i
+ 3];
476 outfiles
[i
] = fopen(outfile_names
[i
], "wb");
477 if (outfiles
[i
] == NULL
) {
478 fprintf(stderr
, "Error. Cannot open output file %s\n\n", outfile_names
[i
]);
481 return (EXIT_FAILURE
);
485 FILE *infile
= fopen(argv
[2], "rb");
486 if (infile
== NULL
) {
487 fprintf(stderr
, "Error. Cannot open input file %s\n\n", argv
[2]);
489 // close file handlers
490 for (uint16_t j
= 0; j
< num_output_files
; j
++) {
496 return (EXIT_FAILURE
);
501 // First call to estimate output size
502 ret
= zlib_decompress(infile
, outfiles
, num_output_files
, &outsize
);
503 if (ret
== EXIT_SUCCESS
) {
504 // Second call to create files
505 ret
= zlib_decompress(infile
, outfiles
, num_output_files
, &outsize
);
508 // close file handlers
510 for (uint16_t j
= 0; j
< num_output_files
; j
++) {
518 } else { // Compress or generate version info
520 bool generate_version_file
= false;
521 uint8_t num_input_files
= 0;
522 if (!strcmp(argv
[1], "-v")) { // generate version info
523 generate_version_file
= true;
524 num_input_files
= argc
- 3;
525 } else { // compress 1..n fpga files
526 num_input_files
= argc
- 2;
529 FILE **infiles
= calloc(num_input_files
, sizeof(FILE *));
530 char **infile_names
= calloc(num_input_files
, sizeof(char *));
531 for (uint8_t i
= 0; i
< num_input_files
; i
++) {
532 infile_names
[i
] = argv
[i
+ (generate_version_file
? 2 : 1)];
533 infiles
[i
] = fopen(infile_names
[i
], "rb");
534 if (infiles
[i
] == NULL
) {
535 fprintf(stderr
, "Error. Cannot open input file %s\n\n", infile_names
[i
]);
538 return (EXIT_FAILURE
);
542 FILE *outfile
= fopen(argv
[argc
- 1], "wb");
543 if (outfile
== NULL
) {
544 fprintf(stderr
, "Error. Cannot open output file %s\n\n", argv
[argc
- 1]);
546 // close file handlers
547 for (uint16_t j
= 0; j
< num_input_files
; j
++) {
553 return (EXIT_FAILURE
);
557 if (generate_version_file
) {
558 ret
= generate_fpga_version_info(infiles
, infile_names
, num_input_files
, outfile
);
560 ret
= zlib_compress(infiles
, num_input_files
, outfile
);
563 // close file handlers
565 for (uint16_t j
= 0; j
< num_input_files
; j
++) {
569 // free file name allocs