2
0

mkfwimage.pl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/perl
  2. use strict;
  3. use integer;
  4. use Digest::MD5;
  5. my $FW_MAGIC = 0x7a07fbd6;
  6. my %datatypes = (
  7. 'end' => 0, # End of data
  8. 'data' => 1, # FPGA flash data
  9. 'target' => 2, # Firmware target string
  10. 'note' => 3, # Informative string
  11. 'espota' => 4, # ESP32 OTA image
  12. 'fpgainit' => 5 # FPGA bypass (transient) image during update
  13. );
  14. my $FDF_OPTIONAL = 0x0001;
  15. my $STRING_MAX_LEN = 4095;
  16. sub getint($) {
  17. my($s) = @_;
  18. return undef
  19. unless ($s =~ /^(([1-9][0-9]+)|(0(x[0-9a-f]+|[0-7]*)))([kmgtpe]?)$/i);
  20. my $o = oct($3) + $2;
  21. my $p = lc($5);
  22. if ($p eq 'k') {
  23. $o <<= 10;
  24. } elsif ($p eq 'm') {
  25. $o <<= 20;
  26. } elsif ($p eq 'g') {
  27. $o <<= 30;
  28. } elsif ($p eq 't') {
  29. $o <<= 40;
  30. } elsif ($p eq 'p') {
  31. $o <<= 50;
  32. } elsif ($p eq 'e') {
  33. $o <<= 60;
  34. }
  35. return $o;
  36. }
  37. sub filelen($) {
  38. my($f) = @_;
  39. my @s = stat($f);
  40. return $s[7];
  41. }
  42. sub output_chunk($$$) {
  43. my($out,$data,$options) = @_;
  44. print $out pack("VvvVV", $FW_MAGIC,
  45. $options->{'type'}, $options->{'flags'},
  46. length($data), $options->{'addr'});
  47. printf STDERR "chunk: type %u flags 0x%x length %u addr 0x%x\n",
  48. $options->{'type'}, $options->{'flags'},
  49. length($data), $options->{'addr'};
  50. print $out $data;
  51. }
  52. if (!scalar(@ARGV)) {
  53. die "Usage: $0 [-o outfile] [options command]...\n".
  54. "Options:\n".
  55. "\t-type datatype\n".
  56. "\t-addr address (or equivalent)\n".
  57. "\t-optional\n".
  58. "\t-required\n".
  59. "Commands:\n".
  60. "\t-file data_file\n".
  61. "\t-str data_string\n";
  62. }
  63. our $outfile;
  64. our $out;
  65. sub delete_out {
  66. close($out);
  67. unlink($outfile);
  68. }
  69. if ($ARGV[0] eq '-o') {
  70. shift @ARGV;
  71. $outfile = shift @ARGV;
  72. }
  73. if ($outfile ne '' && $outfile ne '-') {
  74. open($out, '>', $outfile) or
  75. die "$0: $outfile: $!\n";
  76. $SIG{'INT'} = \&delete_out;
  77. $SIG{'QUIT'} = \&delete_out;
  78. $SIG{'TERM'} = \&delete_out;
  79. $SIG{'__DIE__'} = \&delete_out;
  80. } else {
  81. $outfile = '-';
  82. $out = \*STDOUT;
  83. }
  84. binmode $out;
  85. my %default_options = {
  86. 'type' => $datatypes{'data'},
  87. 'addr' => 0,
  88. 'flags' => 0
  89. };
  90. my $err;
  91. my %options = %default_options;
  92. while (1) {
  93. my $what = shift @ARGV;
  94. last if (!defined($what));
  95. if ($what eq '-type') {
  96. my $arg = lc(shift @ARGV);
  97. $options{'type'} = $datatypes{$arg} || getint($arg);
  98. if (!$arg) {
  99. die "$0: invalid data type: $arg";
  100. }
  101. } elsif ($what eq '-addr') {
  102. my $arg = shift @ARGV;
  103. $options{'addr'} = getint($arg);
  104. } elsif ($what eq '-optional') {
  105. $options{'flags'} |= $FDF_OPTIONAL;
  106. } elsif ($what eq '-required') {
  107. $options{'flags'} &= ~$FDF_OPTIONAL;
  108. } elsif ($what eq '-file') {
  109. my $infile = shift @ARGV;
  110. my $in;
  111. if (!open($in, '<', $infile)) {
  112. die "$0: $infile: $!\n";
  113. }
  114. binmode($in);
  115. my @is = stat($in);
  116. my $data;
  117. my $dlen = read($in, $data, $is[7]);
  118. close($in);
  119. output_chunk($out, $data, \%options);
  120. undef $data;
  121. %options = %default_options;
  122. } elsif ($what eq '-str') {
  123. my $str = shift @ARGV;
  124. if (length($str) > $STRING_MAX_LEN) {
  125. die "$0: string too long\n";
  126. }
  127. output_chunk($out, $str, \%options);
  128. %options = %default_options;
  129. } else {
  130. die "$0: unknown argument: $what\n";
  131. }
  132. }
  133. output_chunk($out, '', {'type' => $datatypes{'end'}});
  134. close($out);