]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
doc: Add complex example of U-Boot interaction
[novaboot.git] / novaboot
index 66ec9815e6d49d669613046470a82fcef5cd7015..e224300ff0c1216cce84a84e27f645f113e16790 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -216,7 +216,7 @@ my %opt_spec;
     "no-uboot"      => sub { undef $uboot; },
     "uboot-addr=s"   => \%uboot_addr,
     "uboot-cmd=s"    => \$uboot_cmd,
-    "uboot-init=s"   => \@uboot_init,
+    "uboot-init=s"   => sub { push @uboot_init, { command => $_[1] }; },
     "h"             => \$help,
     "help"          => \$man,
     );
@@ -296,7 +296,7 @@ my @scripts;
 my $file;
 my $EOF;
 my $last_fn = '';
-my ($modules, $variables, $generated, $continuation) = ([], {}, []);
+my ($modules, $variables, $generated, $copy, $continuation) = ([], {}, [], []);
 my $skip_reading = defined($on_opt) || defined($off_opt);
 while (!$skip_reading && ($_ = <>)) {
     if ($ARGV ne $last_fn) { # New script
@@ -306,7 +306,9 @@ while (!$skip_reading && ($_ = <>)) {
        push @scripts, { 'filename' => $ARGV,
                         'modules' => $modules = [],
                         'variables' => $variables = {},
-                        'generated' => $generated = []};
+                        'generated' => $generated = [],
+                        'copy' => $copy = [],
+       };
 
     }
     chomp();
@@ -339,36 +341,45 @@ while (!$skip_reading && ($_ = <>)) {
        push(@exiton, $2) if ($1 eq "EXITON");
        next;
     }
-    if (s/^load *//) {         # Load line
+    sub process_load_copy($) {
        die("novaboot: '$last_fn' line $.: Missing file name\n") unless /^[^ <]+/;
        if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
-           push @$modules, "$1$2";
            $file = [];
            push @$generated, {filename => $1, content => $file};
            $EOF = $3;
-           next;
+           return "$1$2";
        }
        if (/^([^ ]*)(.*?)[[:space:]]*< ?(.*)$/) { # Command substitution
-           push @$modules, "$1$2";
            push @$generated, {filename => $1, command => $3};
-           next;
+           return "$1$2";
        }
-       push @$modules, $_;
+       return $_;
+    }
+    if (s/^load *//) {         # Load line
+       push @$modules, process_load_copy($_);
+       next;
+    }
+    if (s/^copy *//) {         # Copy line
+       push @$copy, process_load_copy($_);
        next;
     }
     if (/^run (.*)/) {         # run line
        push @$generated, {command => $1};
        next;
     }
-    if (/^uboot(?::([0-9]+)s)? (.*)/) {        # uboot line
+    if (/^uboot(?::([0-9]+)s)? +(< *)?(.*)/) { # uboot line
        # TODO: If U-Boot supports some interactive menu, it might
        # make sense to store uboot lines per novaboot script.
-       if ($1) {               # Command with explicit timeout
-           push @uboot_init, { command => $2,
-                               timeout => $1 };
-       } else {                # Command without explicit timeout
-           push @uboot_init, $2;
+       my ($timeout, $redir, $string, $dest) = ($1, $2, $3);
+       if ($string =~ /(.*) *> *(.*)/) {
+           $string = $1;
+           $dest = $2;
        }
+       push @uboot_init, { command => $redir ? "" : $string,
+                           system =>  $redir ? $string : "",
+                           timeout => $timeout,
+                           dest => $dest,
+       };
        next;
     }
 
@@ -874,7 +885,7 @@ foreach my $script (@scripts) {
            $path = $hostname;
            $hostname = "";
        }
-       my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs)));
+       my $files = join(" ", map({ ($file) = m/([^ ]*)/; $file; } ( @$modules, @bootloader_configs, @$copy)));
        map({ my $file = (split)[0]; die "$file: $!" if ! -f $file; } @$modules);
        my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb';
        my $progress = $istty ? "--progress" : "";
@@ -1080,15 +1091,17 @@ if (defined $uboot) {
                 $uboot_prompt) || die "No U-Boot prompt deteceted";
     foreach my $cmdspec (@uboot_init) {
        my ($cmd, $timeout);
-       if (ref($cmdspec) eq "HASH") {
-           $cmd = $cmdspec->{command};
-           $timeout = $cmdspec->{timeout};
+       die "Internal error - please report a bug" unless ref($cmdspec) eq "HASH";
+
+       if ($cmdspec->{system}) {
+           $cmd = `$cmdspec->{system}`;
        } else {
-           $cmd = $cmdspec;
-           $timeout = 10;
+           $cmd = $cmdspec->{command};
        }
+       $timeout = $cmdspec->{timeout} // 10;
+
        if ($cmd =~ /\$NB_MYIP/) {
-           my $ip = (grep /inet /, `ip addr show $netif`)[0] || die "Problem determining our IP address";
+           my $ip = (grep /inet /, `ip addr show $netif`)[0] || die "Problem determining IP address of $netif";
            $ip =~ s/\s*inet ([0-9.]*).*/$1/;
            $cmd =~ s/\$NB_MYIP/$ip/g;
        }
@@ -1099,7 +1112,18 @@ if (defined $uboot) {
        }
        chomp($cmd);
        $exp->send("$cmd\n");
-       $exp->expect($timeout, $uboot_prompt) || die "U-Boot prompt timeout";
+
+       my ($matched_pattern_position, $error,
+           $successfully_matching_string,
+           $before_match, $after_match) =
+               $exp->expect($timeout, $uboot_prompt);
+       die "No U-Boot prompt: $error" if $error;
+
+       if ($cmdspec->{dest}) {
+           open(my $fh, ">", $cmdspec->{dest}) or die "Cannot open '$cmdspec->{dest}': $!";
+           print $fh $before_match;
+           close($fh);
+       }
     }
 
     # Load files if there are some load lines in the script
@@ -1402,9 +1426,10 @@ command line. This option can appear multiple times.
 
 =item -b, --bender
 
-Use F<bender> chainloader. Bender scans the PCI bus for PCI serial
-ports and stores the information about them in the BIOS data area for
-use by the kernel.
+Use L<bender|https://github.com/TUD-OS/morbo/blob/master/standalone/bender.c>
+chainloader. Bender scans the PCI bus for PCI serial ports and stores
+the information about them in the BIOS data area for use by the
+kernel.
 
 =item --chainloader=I<chainloader>
 
@@ -1880,7 +1905,14 @@ Lines of the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
 expression) assign values to internal variables. See L</VARIABLES>
 section.
 
-Lines starting with C<load> keyword represent modules to boot. The
+Otherwise, the first word on the line defines the meaning of the line.
+The following keywords are supported:
+
+=over 4
+
+=item C<load>
+
+These lines represent modules to boot. The
 word after C<load> is a file name (relative to the build directory
 (see B<--build-dir>) of the module to load and the remaining words are
 passed to it as the command line parameters.
@@ -1895,20 +1927,49 @@ that line. The SRCDIR variable in CMD's environment is set to the
 absolute path of the directory containing the interpreted novaboot
 script.
 
+=item C<copy>
+
+These lines are similar to C<load> lines. The
+file mentioned there is copied to the same place as in case of C<load>
+(e.g. tftp server), but the file is not used in the bootloader
+configuration. Such a file can be used by the target for other
+purposed than booting, e.g. at OS runtime or for firmware update.
+
+=item C<run>
+
 Lines starting with C<run> keyword contain shell commands that are run
 during file generation phase. This is the same as the "< CMD" syntax
 for C<load> keyboard except that the command's output is not
 redirected to a file. The ordering of commands is the same as they
 appear in the novaboot script.
 
-Lines starting with C<uboot> represent U-Boot commands that are sent
-to the target if B<--uboot> option is given. Having a U-Boot line in
-the novaboot script is the same as passing an equivalent
-B<--uboot-init> option to novaboot. The C<uboot> keyword can be
-suffixed with timeout specification. The syntax is C<uboot:Ns>, where
-C<N> is the whole number of seconds. If the U-Boot command prompt does
-not appear before the timeout, novaboot fails. The default timeout is
-10 seconds.
+=item C<uboot>
+
+These lines represent U-Boot commands that are sent to the target if
+B<--uboot> option is given. Having a U-Boot line in the novaboot
+script is the same as giving B<--uboot-init> option to novaboot. The
+following syntax variants are supported:
+
+
+  uboot[:<timeout>] <string> [> <file>]
+  uboot[:<timeout>] < <shell> [> <file>]
+
+C<string> is the literal U-Boot command.
+
+The C<uboot> keyword can be suffixed with timeout specification. The
+syntax is C<uboot:Ns>, where C<N> is the whole number of seconds. If
+the U-Boot command prompt does not appear before the timeout, novaboot
+fails. The default timeout is 10 seconds.
+
+In the second variant with the C<<> character the shell code is
+executed and its standard output is sent to U-Boot. Example:
+
+  uboot < printf "mmc write \$loadaddr 1 %x" $(($(/usr/bin/stat -c%s rootfs.ext4) / 512))
+
+When C<E<gt> file> part is present, the output of the U-Boot command
+is written into the given file.
+
+=back
 
 Example (Linux):
 
@@ -1934,6 +1995,28 @@ F<hello.nulconfig>. sigma0 receives some command line parameters and
 F<hello.nulconfig> file is generated on the fly from the lines between
 C<<<EOF> and C<EOF>.
 
+Example (Zynq system update via U-Boot):
+
+  #!/usr/bin/env novaboot
+
+  uboot dhcp
+
+  # Write kernel to FAT filesystem on the 1st SD card partition
+  run mkimage -f uboot-image.its image.ub
+  copy image.ub
+  uboot:60s tftpboot ${loadaddr} $NB_PREFIX/image.ub
+  uboot fatwrite mmc 0:1 ${loadaddr} image.ub $filesize
+  uboot set bootargs console=ttyPS0,115200 root=/dev/mmcblk0p2
+
+  # Write root FS image to the 2nd SD card partition
+  copy rootfs/images/rootfs.ext4
+  uboot:60s tftpboot ${loadaddr} $NB_PREFIX/rootfs/images/rootfs.ext4
+  uboot mmc part > mmc-part.txt
+  uboot < printf "mmc write \$loadaddr %x %x" $(awk '{ if ($1 == "2") { print $2 }}' mmc-part.txt) $(($(/usr/bin/stat -L --printf=%s rootfs/images/rootfs.ext4) / 512))
+
+  UBOOT_CMD=boot
+
+
 =head2 VARIABLES
 
 The following variables are interpreted in the novaboot script:
@@ -2070,7 +2153,8 @@ with the B<--target> command line option.
 
 =item NOVABOOT_BENDER
 
-Defining this variable has the same meaning as B<--bender> option.
+Defining this variable has the same effect as using B<--bender>
+option.
 
 =back