2
0

mkfwimage.pl 3.5 KB

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