summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2022-09-02 02:38:10 -0700
committerH. Peter Anvin <hpa@zytor.com>2022-09-02 02:38:10 -0700
commit5e6c80650268b9ae9448c6dca9c076a03c5d1749 (patch)
treebc7317867b528f14604b2e6ac2df2d05764e1d77
parent7b5981fe5bdbdd070c5f736450e38f05a670cd49 (diff)
downloadblinktest-5e6c80650268b9ae9448c6dca9c076a03c5d1749.tar.gz
blinktest-5e6c80650268b9ae9448c6dca9c076a03c5d1749.tar.xz
blinktest-5e6c80650268b9ae9448c6dca9c076a03c5d1749.zip
flashmax.pl: support multiversion (v2) firmware files
Support multiversion firmware files for "make upload PORT=" or calling flashmax.pl directly. flashmax.pl can now also write the board revision to flash if needed. This should conclude the multi-version firmware support.
-rw-r--r--Makefile8
-rwxr-xr-xtools/flashmax.pl191
2 files changed, 181 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index 0b480d0..2532fac 100644
--- a/Makefile
+++ b/Makefile
@@ -84,9 +84,7 @@ ifeq ($(PORT),)
# Generic upload for newer firmware
upload:
- @echo 'NOT IMPLEMENTED YET, should be:'
- @echo '$(CURL) -v --data-binary @fpga/output/$(PROJECT).fw http://$(ip)/sys/fwupdate'
- @exit 1
+ $(CURL) -v --data-binary @fpga/output/$(PROJECT).fw 'http://$(ip)/sys/fwupdate'
# Version-specific uploads for older firmware
upload-v%:
@@ -102,9 +100,7 @@ else
# Generic upload for newer firmware
upload:
- @echo 'NOT IMPLEMENTED YET, should be:'
- @echo '$(PERL) ./tools/flashmax.pl fpga/output/$(PROJECT).fw $(PORT) $(FLASHOPT)'
- @exit 1
+ $(PERL) ./tools/flashmax.pl fpga/output/$(PROJECT).fw '$(PORT)' $(FLASHOPT)
# Version-specific uploads for older firmware
upload-v%:
diff --git a/tools/flashmax.pl b/tools/flashmax.pl
index bff5331..cda8ed3 100755
--- a/tools/flashmax.pl
+++ b/tools/flashmax.pl
@@ -9,12 +9,12 @@ use Time::HiRes qw(usleep tv_interval);
use Digest::CRC qw(crc32);
use v5.10; # For "state"
-my $esptool = ($^O eq 'MSWin32') ? 'esptool.exe' : 'esptool.py';
+my $esptool = 'esptool.py';
$esptool = $ENV{'ESPTOOL'} || $esptool;
-my $esp_retries = 5;
+my $esp_retries = 10;
-my $FW_MAGIC = 0x7a07fbd6;
+my @FW_MAGIC = (undef, 0x7a07fbd6, 0xa924ed0b);
my %datatypes = (
'end' => 0, # End of data
@@ -33,10 +33,14 @@ foreach my $t (keys(%datatypes)) {
$type[$datatypes{$t}] = $t;
}
-my $FDF_OPTIONAL = 0x0001;
+my $FDF_OPTIONAL = 0x0001;
+my $FDF_PRETARGET = 0x0002;
my $STRING_MAX_LEN = 4095;
+my $boardinfo_addr = 0;
+my $boardinfo_len = 4096;
+
# For debug; DC1-4 replaced with functional names
my @ascii = qw(NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
DLE XON WRST XOFF WGO NAK SYN ETB CAN EM SUB ESC FS GS RS US);
@@ -98,6 +102,12 @@ sub getint($) {
return $o;
}
+sub match_version($$) {
+ my($ver,$pattern) = @_;
+
+ return 1; # FIX THIS
+}
+
sub filelen($) {
my($f) = @_;
my @s = stat($f);
@@ -178,6 +188,7 @@ sub run_esptool($$$$@)
print STDERR "ok\n";
last;
} elsif ($retries) {
+ @output = ();
print STDERR "failed, retrying\n";
usleep(1000000);
} else {
@@ -191,15 +202,106 @@ sub run_esptool($$$$@)
return %outinfo;
}
+sub target_string_valid($)
+{
+ my($v) = @_;
+
+ return $v =~ /^(\S+) v((?:0|[1-9][0-9]*)(?:\.0|\.[1-9][0-9]*)*)(?: ([a-zA-Z0-9]*))?$/;
+}
+
+sub match_version($$)
+{
+ my($version,$pattern) = @_;
+
+ my @vv = target_string_valid($version);
+
+ return 0 unless (defined($vv[0]));
+
+ my $v_board = $vv[0];
+ my @v_ver = split(/\./, $vv[1]);
+ my $v_flags = $vv[2];
+
+ return 1 if ($pattern eq $v_board); # Board only matchall pattern
+
+ if ($pattern !~ /^(\S+) v((?:0|[1-9][0-9]*)(?:\.\.0|\.[1-9][0-9]*)*)(?:(\-)((?:0|[1-9][0-9]*)(?:\.\.0|\.[1-9][0-9]*)*))?(?: ([\+\-a-zA-Z0-9]*))?$/) {
+ return 0;
+ }
+
+ return 0 if ($1 ne $v_board);
+
+ my @p_min = split(/\./, $2);
+ my @p_max = split(/\./, $3 eq '' ? $2 : $4);
+ my $p_flags = $5;
+
+ while (scalar(@p_min) || scalar(@p_max)) {
+ my $mi = shift(@p_min);
+ my $ma = shift(@p_max);
+ my $ve = shift(@v_ver) || 0;
+
+ return 0 if (defined($mi) && $ve < $mi);
+ return 0 if (defined($ma) && $ve > $ma);
+ }
+
+ my $flag_pol = 1;
+
+ foreach my $c (split(//, $p_flags)) {
+ if ($c eq '-') {
+ $flag_pol = 0;
+ } elsif ($c eq '+') {
+ $flag_pol = 1;
+ } else {
+ return 0 if ((index($v_flags, $c) != -1) != $flag_pol);
+ }
+ }
+
+ return 1;
+}
+
+sub get_target_board($$)
+{
+ my($port,$boardinfo) = @_;
+
+ run_esptool($port, { 'before' => 'default_reset', 'after' => 'hard_reset' },
+ 'read_flash', { },
+ ''.$boardinfo_addr, ''.$boardinfo_len, $boardinfo);
+
+ open(my $bi, '<:raw', $boardinfo) or return undef;
+ my $bid;
+ read($bi, $bid, $boardinfo_len);
+ close($bi);
+ return undef if (length($bid) != $boardinfo_len);
+
+ my @bh = unpack('VVVVZ[256]', $bid);
+ if ($bh[0] == 0x6682df97 && $bh[1] == 0xe2a0d506) {
+ # Standard format board_info structure
+ substr($bid, 12, 4) = "\0\0\0\0"; # Clear the CRC field
+ if ($bh[2] >= 16 && $bh[2] <= $boardinfo_len &&
+ crc32(substr($bid, 0, $bh[2])) == $bh[3]) {
+ return $bh[4];
+ }
+ } elsif ($bid =~ /^([[:print:]]+)\0/) {
+ # Preliminary board_info (only version string)
+ return $1;
+ }
+
+ return undef;
+}
+
my @args = @ARGV;
my $esponly = 0;
my $file;
+my $target_board = undef;
+my $setver = 0;
+
while (1) {
$file = shift(@args);
last if ($file !~ /^\-/);
if ($file eq '--esponly') {
$esponly = 1;
+ } elsif ($file eq '--setver') {
+ $target_board = shift(@args);
+ $setver = defined($target_board);
} elsif ($file eq '--') {
$file = shift(@args);
last;
@@ -211,7 +313,7 @@ while (1) {
my $port = shift(@args);
if (!defined($port)) {
- die "Usage: $0 file.fw port [esptool options...]\n";
+ die "Usage: $0 [--esponly][--setver versionoptions] file.fw port [esptool_options...]\n";
}
if (!File::Spec->file_name_is_absolute($port)) {
@@ -227,10 +329,38 @@ if (!File::Spec->file_name_is_absolute($port)) {
}
print STDERR "Using serial port device $port\n";
+my $td = File::Temp->newdir(CLEANUP => 1);
+my $boardinfo = File::Spec->catfile($td, 'boardinfo.bin');
+
+my @espfiles = ();
+
+if (defined($target_board)) {
+ # Forcibly write target board version
+ if (!target_string_valid($target_board)) {
+ die "$0: $port: invalid firmware target string: $target_board\n";
+ }
+
+ open(my $bi, '>:raw', $boardinfo)
+ or die "$0: $port: $boardinfo: $!\n";
+ my $bid = $target_board . "\0";
+ $bid .= "\xff" x ($boardinfo_len - length($bid));
+ print $bi $bid;
+ close($bi);
+ push(@espfiles, ''.$boardinfo_addr, $boardinfo);
+} else {
+ # Get the board version from target flash
+
+ $target_board = get_target_board($port, $boardinfo);
+ if (!defined($target_board) || !target_string_valid($target_board)) {
+ die "$0: $port: board version not programmed, specify with --setver\n";
+ }
+}
+
open(my $fw, '<:gzip', $file)
or die "$0: $file: $!\n";
my @chunks = ();
+my $version_match = undef;
my $err = 0;
@@ -239,8 +369,21 @@ while (read($fw, $hdr, 16) == 16) {
# magic type flags data_len addr
my @h = unpack('VvvVV', $hdr);
my $c = { 'hdr' => $hdr, 'magic' => $h[0], 'type' => $h[1],
- 'flags' => $h[2], 'len' => $h[3], 'addr' => $h[4] };
- if ($c->{'magic'} != $FW_MAGIC) {
+ 'flags' => $h[2], 'len' => $h[3], 'addr' => $h[4],
+ 'vmatch' => 0, 'vmask' => 0, 'vmin' => 0, 'vmax' => 0xffff };
+ if ($c->{'magic'} == $FW_MAGIC[2]) {
+ if (read($fw, $hdr, 16) != 16) {
+ print STDERR "$0: $file: short header read\n";
+ $err = 1;
+ last;
+ }
+ my @h = unpack('VVvvV', $hdr);
+ $c->{'hdr'} .= $hdr;
+ $c->{'vmatch'} = $h[0];
+ $c->{'vmask'} = $h[1];
+ $c->{'vmin'} = $h[2];
+ $c->{'vmax'} = $h[3];
+ } elsif ($c->{'magic'} != $FW_MAGIC[1]) {
print STDERR "$0: $file: bad chunk magic\n";
$err = 1;
last;
@@ -255,6 +398,31 @@ while (read($fw, $hdr, 16) == 16) {
last;
}
$c->{'data'} = $d;
+
+ if ($t eq 'target') {
+ my $is_match = match_version($target_board, $d);
+ if ($c->{'magic'} == $FW_MAGIC[1] || $is_match) {
+ $version_match = $c;
+ }
+ print STDERR "$0: $file: supports hardware: $d",
+ ($is_match ? ' (match)' : ''), "\n";
+ } elsif ($t eq 'end' || $t eq 'note' || ($c->{'flags'} & $FDF_PRETARGET)) {
+ # Not filtered
+ } else {
+ if (!defined($version_match)) {
+ print STDERR "$0: $file: hardware version $target_board not supported\n";
+ $err = 1;
+ last;
+ }
+
+ if ($c->{'vmin'} > $version_match->{'vmax'} ||
+ $c->{'vmax'} < $version_match->{'vmin'} ||
+ (($c->{'vmatch'} ^ $version_match->{'vmatch'}) & $c->{'vmask'})) {
+ # Not applicable to this board
+ next;
+ }
+ }
+
push(@chunks, $c);
last if ($t eq 'end'); # End of stream
@@ -263,9 +431,6 @@ while (read($fw, $hdr, 16) == 16) {
close($fw);
exit $err if ($err);
-my $td = File::Temp->newdir(CLEANUP => 1);
-
-my @espfiles = ();
my %espopt = ('before' => 'default_reset', 'after' => 'hard_reset',
'baud' => 115200, 'port' => undef, 'chip' => undef,
'flash_mode' => undef, 'flash_freq' => undef,
@@ -292,6 +457,8 @@ foreach my $c ( @chunks ) {
print $tf $c->{'data'};
close($tf);
push(@espfiles, '0x'.$addr, $tff);
+ } elsif ($t eq 'note' || ($t eq 'target' && $c != $version_match)) {
+ # Skip
} else {
print $fpgafh $c->{'hdr'};
print $fpgafh $c->{'data'};
@@ -308,8 +475,8 @@ foreach my $e (keys %espopt) {
}
}
-run_esptool($port, { hgrep sub {!/^flash_/}, %espopt },
- 'write_flash', { hgrep sub {/^flash_/}, %espopt },
+run_esptool($port, { hgrep {!/^flash_/} %espopt },
+ 'write_flash', { hgrep {/^flash_/} %espopt },
'-z', @espfiles);
my $SerialPort = eval {