%CFG::targets = (
'qemu' => '--qemu',
"tud" => '--server=erwin.inf.tu-dresden.de:~sojka/boot/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --grub --grub-prefix=(nd)/tftpboot/sojka/novaboot --grub-preamble="timeout 0" --concat --iprelay=141.76.48.80:2324 --scriptmod=s/\\\\bhostserial\\\\b/hostserialpci/g',
- "novabox" => '--server=rtime.felk.cvut.cz:/srv/tftp/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --pulsar --iprelay=147.32.86.92:2324',
+ "novabox" => '--server=rtime.felk.cvut.cz:/srv/tftp/novaboot --rsync-flags="--chmod=Dg+s,ug+w,o-w,+rX --rsync-path=\"umask 002 && rsync\"" --pulsar --iprelay=147.32.87.159:2324',
"localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog=" set root=\'(hd0,msdos1)\'"',
"ryu" => '--uboot --uboot-init="mw f0000b00 \${psc_cfg}; sleep 1" --uboot-addr kernel=800000 --uboot-addr ramdisk=b00000 --uboot-addr fdt=7f0000',
"ryuglab" => '--target ryu --server=pc-sojkam.felk.cvut.cz:/srv/tftp --remote-cmd="ssh -tt pc-sojkam.felk.cvut.cz \"sterm -d -s 115200 /dev/ttyUSB0\""',
my $explicit_target = $ENV{'NOVABOOT_TARGET'};
GetOptions ("target|t=s" => \$explicit_target);
-my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, $exiton_timeout, @expect_raw, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $interaction, $iprelay, $iso_image, $interactive, $kernel_opt, $make, $man, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $remote_expect, $remote_expect_silent, $reset, $reset_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, $exiton_timeout, @expect_raw, $final_eol, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $interaction, $iprelay, $iso_image, $interactive, $kernel_opt, $make, $man, $netif, $no_file_gen, $off_opt, $on_opt, $pulsar, $pulsar_root, $qemu, $qemu_append, $qemu_flags_cmd, $remote_cmd, $remote_expect, $remote_expect_silent, $reset, $reset_cmd, $reset_send, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+# Default values of certain command line options
%uboot_addr = (
'kernel' => '${kernel_addr_r}',
'ramdisk' => '${ramdisk_addr_r}',
'fdt' => '${fdt_addr_r}',
);
-
$rsync_flags = '';
$rom_prefix = 'rom://';
$stty = 'raw -crtscts -onlcr 115200';
$reset = 1; # Reset target by default
$interaction = 1; # Perform target interaction by default
+$final_eol = 1;
+$netif = 'eth0';
my @expect_seen = ();
sub handle_expect
"expect=s" => \&handle_expect,
"expect-re=s" => \&handle_expect,
"expect-raw=s" => sub { my ($n, $v) = @_; unshift(@expect_raw, eval($v)); },
+ "final-eol!" => \$final_eol,
"gen-only" => \$gen_only,
"grub|g:s" => \$grub_config,
"grub-preamble=s"=> \$grub_preamble,
"interactive|i" => \$interactive,
"name=s" => \$config_name_opt,
"make|m:s" => \$make,
+ "netif=s" => \$netif,
"no-file-gen" => \$no_file_gen,
"off" => \$off_opt,
"on" => \$on_opt,
"remote-expect-silent=s"=> sub { $remote_expect=$_[1]; $remote_expect_silent=1; },
"reset!" => \$reset,
"reset-cmd=s" => \$reset_cmd,
+ "reset-send=s" => \$reset_send,
"rsync-flags=s" => \$rsync_flags,
"scons:s" => \$scons,
"scriptmod=s" => \@scriptmod,
"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,
);
push(@target_expanded, @$remaining_args);
$t = $explicit_target;
}
+
+ (undef, my @args) = @ARGV;
+ @args = (@target_expanded, @args);
+ print STDERR "novaboot: Effective options: @args\n";
+
Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
GetOptionsFromArray(\@target_expanded, %opt_spec) or die ("Error in target definition");
}
my $file;
my $EOF;
my $last_fn = '';
-my ($modules, $variables, $generated, $continuation) = ([], {}, []);
+my ($modules, $variables, $generated, $copy, $chainload, $continuation) = ([], {}, [], []);
my $skip_reading = defined($on_opt) || defined($off_opt);
while (!$skip_reading && ($_ = <>)) {
if ($ARGV ne $last_fn) { # New script
push @scripts, { 'filename' => $ARGV,
'modules' => $modules = [],
'variables' => $variables = {},
- 'generated' => $generated = []};
+ 'generated' => $generated = [],
+ 'copy' => $copy = [],
+ 'chainload' => $chainload = [],
+ };
}
chomp();
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 (s/^chld *//) { # Chainload line
+ push @$chainload, 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;
}
return $filename;
}
-sub generate_pulsar_config($$)
+sub generate_pulsar_config($$$)
{
- my ($filename, $modules_ref) = @_;
+ my ($filename, $modules_ref, $chainload_ref) = @_;
open(my $fg, '>', $filename) or die "$filename: $!";
print $fg "root $pulsar_root\n" if defined $pulsar_root;
- my $first = 1;
- my ($kbin, $kcmd);
- foreach (@$modules_ref) {
- if ($first) {
- $first = 0;
- ($kbin, $kcmd) = split(' ', $_, 2);
- $kcmd = '' if !defined $kcmd;
- } else {
- my @args = split;
- s|\brom://|$rom_prefix|g;
- print $fg "load $_\n";
+ if (scalar(@$chainload_ref) > 0) {
+ print $fg "chld $$chainload_ref[0]\n";
+ } else {
+ my $first = 1;
+ my ($kbin, $kcmd);
+ foreach (@$modules_ref) {
+ if ($first) {
+ $first = 0;
+ ($kbin, $kcmd) = split(' ', $_, 2);
+ $kcmd = '' if !defined $kcmd;
+ } else {
+ my @args = split;
+ s|\brom://|$rom_prefix|g;
+ print $fg "load $_\n";
+ }
}
+ # Put kernel as last - this is needed for booting Linux and has no influence on non-Linux OSes
+ print $fg "exec $kbin $kcmd\n";
}
- # Put kernel as last - this is needed for booting Linux and has no influence on non-Linux OSes
- print $fg "exec $kbin $kcmd\n";
close($fg);
print("novaboot: Created $builddir/$filename\n");
return $filename;
};
}
+if (defined $reset_send) {
+ $target_reset = sub {
+ $reset_send =~ s/\\n/\n/g;
+ $exp->send($reset_send);
+ };
+}
+
if (defined $on_opt && defined $target_power_on) {
&$target_power_on();
exit;
my @bootloader_configs;
push @bootloader_configs, generate_grub_config($grub_config, $config_name, $prefix, $modules, $grub_preamble) if (defined $grub_config);
push @bootloader_configs, generate_grub2_config($grub2_config, $config_name, $prefix, $modules, $grub_preamble, $grub2_prolog) if (defined $grub2_config);
- push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules) if (defined $pulsar);
+ push @bootloader_configs, generate_pulsar_config('config-'.($pulsar||'novaboot'), $modules, $chainload) if (defined $pulsar);
### Run scons or make
{
- my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
+ my @all;
+ push @all, @$modules;
+ push @all, @$copy;
+ push @all, @$chainload;
+ my @files = map({ ($file) = m/([^ ]*)/; $file; } @all);
+
# Filter-out generated files
my @to_build = grep({ my $file = $_; !scalar(grep($file eq ($$_{filename} || ''), @$generated)) } @files);
$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" : "";
- system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
- if ($server =~ m|/\$NAME$| && $concat) {
- my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
- system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
+ if ($files) {
+ system_verbose("rsync $progress -RLp $rsync_flags $files $real_server");
+ if ($server =~ m|/\$NAME$| && $concat) {
+ my $cmd = join("; ", map { "( cd $path/.. && cat */$_ > $_ )" } @bootloader_configs);
+ system_verbose($hostname ? "ssh $hostname '$cmd'" : $cmd);
+ }
}
}
push(@qemu_flags, ('-dtb', $dtb)) if $dtb;
}
}
- push(@qemu_flags, qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
+ if (!grep /^-serial$/, @qemu_flags) {
+ push(@qemu_flags, qw(-serial stdio)); # Redirect serial output (for collecting test restuls)
+ }
unshift(@qemu_flags, ('-name', $config_name));
print STDERR "novaboot: Running: ".shell_cmd_string($qemu, @qemu_flags)."\n";
$exp = Expect->spawn(($qemu, @qemu_flags)) || die("exec() failed: $!");
system_verbose('mkdir -p tftpboot');
generate_grub_config("tftpboot/os-menu.lst", $config_name, "(nd)", \@$modules, "timeout 0");
open(my $fh, '>', 'dhcpd.conf');
- my $mac = `cat /sys/class/net/eth0/address`;
+ my $mac = `cat /sys/class/net/$netif/address`;
chomp $mac;
print $fh "subnet 10.23.23.0 netmask 255.255.255.0 {
range 10.23.23.10 10.23.23.100;
fixed-address 10.23.23.1;
}";
close($fh);
- system_verbose("sudo ip a add 10.23.23.1/24 dev eth0;
- sudo ip l set dev eth0 up;
+ system_verbose("sudo ip a add 10.23.23.1/24 dev $netif;
+ sudo ip l set dev $netif up;
sudo touch dhcpd.leases");
# We run servers by forking ourselves, because the servers end up
$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 eth0`)[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;
}
}
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);
+ }
}
- # Boot the system if there are some load lines in the script
- if ((scalar(@$modules) > 0 && !$variables->{NO_BOOT}) ||
- defined $uboot_cmd) {
+ # Load files if there are some load lines in the script
+ if (scalar(@$modules) > 0 && !$variables->{NO_BOOT}) {
my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
my $dtb;
@$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
$exp->send("setenv bootargs $kcmd\n");
$exp->expect(5, $uboot_prompt) || die "U-Boot prompt timeout";
- $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
+ }
+ $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
+ if (!$variables->{NO_BOOT} && $uboot_cmd ne '') {
$uboot_cmd =~ s/\$kernel_addr/$uboot_addr{kernel}/g;
$uboot_cmd =~ s/\$ramdisk_addr/$uboot_addr{ramdisk}/g;
$uboot_cmd =~ s/\$fdt_addr/$uboot_addr{fdt}/g;
#use Data::Dumper;
#print Dumper(\@expect_raw);
$exp->expect(undef, @expect_raw) if @expect_raw;
+
+ $^W = 0; # Suppress Expect warning: handle id(3) is not a tty. Not changing mode at /usr/share/perl5/Expect.pm line 393, <> line 8.
Expect::interconnect(@inputs) unless defined($exp->exitstatus);
+ $^W = 1;
}
}
}
# Always finish novaboot output with newline
-print "\n";
+print "\n" if $final_eol;
## Documentation
+=encoding utf8
=head1 NAME
novaboot - Boots a locally compiled operating system on a remote
=head1 DESCRIPTION
-This program makes booting of a locally compiled operating system (OS)
+Novaboot makes booting of a locally compiled operating system (OS)
(e.g. NOVA or Linux) on remote targets as simple as running a program
locally. It automates things like copying OS images to a TFTP server,
generation of bootloader configuration files, resetting of target
hardware or redirection of target's serial line to stdin/out. Novaboot
-is highly configurable and it makes it easy to boot a single image on
+is highly configurable and makes it easy to boot a single image on
different targets or different images on a single target.
-Novaboot operation is controlled by command line options and by a so
-called novaboot script, which can be thought as a generalization of
-bootloader configuration files (see L</"NOVABOOT SCRIPT SYNTAX">).
-Typical way of using novaboot is to make the novaboot script
-executable and set its first line to I<#!/usr/bin/env novaboot>. Then,
-booting a particular OS configuration becomes the same as executing a
-local program - the novaboot script.
+Novaboot operation is controlled by configuration files, command line
+options and by a so called novaboot script, which can be thought as a
+generalization of bootloader configuration files (see L</"NOVABOOT
+SCRIPT SYNTAX">). Typical way of using novaboot is to make the
+novaboot script executable and set its first line to I<#!/usr/bin/env
+novaboot>. Then, booting a particular OS configuration becomes the
+same as executing a local program – the novaboot script.
Novaboot uses configuration files to, among other things, define
command line options needed for different targets. Users typically use
Internally, this option expands to the pre-configured options.
Configuration files are searched at multiple places, which allows to
have per-system, per-user or per-project configurations. Configuration
-file syntax is described in section L</"CONFIGURATION FILE">.
+file syntax is described in section L</"CONFIGURATION FILES">.
Simple examples of using C<novaboot>:
=item 1.
-Run an OS in Qemu. This is can be specified with the B<--qemu> option.
-Thus running C<novaboot --qemu myos> (or C<./myos --qemu> as described
-above) will run Qemu and make it boot the configuration specified in
-the F<myos> script.
+Run an OS in Qemu. This can be specified with the B<--qemu> option.
+Thus running
+
+ novaboot --qemu myos
+
+(or C<./myos --qemu> as described above) will run Qemu and make it
+boot the configuration specified in the F<myos> script.
=item 2.
./myos --grub2 --server=192.168.1.1:/tftp --iprelay=192.168.1.2
+Alternatively, you can put these switches to the configuration file
+and run:
+
+ ./myos --target mytarget
+
=item 3.
Run DHCP and TFTP server on developer's machine to boot the target
=head2 Configuration reading phase
After starting, novaboot reads configuration files. Their content is
-described in section L</"CONFIGURATION FILE">. By default,
+described in section L</"CONFIGURATION FILES">. By default,
configuration is read from multiple locations. First from the system
configuration directory (F</etc/novaboot.d/>), second from the user
configuration file (F<~/.config/novaboot>) and third from F<.novaboot>
This option serves as a user configurable shortcut for other novaboot
options. The effect of this option is the same as specifying the
options stored in the C<%targets> configuration variable under key
-I<target>. See also L</"CONFIGURATION FILE">.
+I<target>. See also L</"CONFIGURATION FILES">.
When this option is not given, novaboot tries to determine the target
to use from either B<NOVABOOT_TARGET> environment variable or from
=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>
Port to run the TFTP server on. Implies B<--tftp>.
+=item --netif=I<network interface>
+
+Network interface used to deploy files to the target. Default value is
+I<eth0>. This influences the configuration of the DHCP server started
+by B<--dhcp-tftp> and the value that B<$NB_MYIP> get replaced with in
+U-Boot conversation.
+
=item --iso[=filename]
Generates the ISO image that boots NOVA system via GRUB. If no filename
At this point, the target is reset (or switched on/off). There is
several ways how this can be accomplished. Resetting a physical target
can currently be accomplished by the following options: B<--amt>,
-B<--iprelay>, B<--reset-cmd>.
+B<--iprelay>, B<--reset-cmd> and B<--reset-send>.
=over 8
Command that resets the target.
+=item --reset-send=I<string>
+
+Reset the target by sending the given I<string> to the remote serial
+line. "\n" sequences are replaced with newline character.
+
=item --no-reset, --reset
Disable/enable resetting of the target.
commands from each option novaboot waits for U-Boot I<prompt>.
If the command contains string I<$NB_MYIP> then this string is
-replaced by IPv4 address of eth0 interface. Similarly I<$NB_PREFIX> is
-replaced with prefix given by B<--prefix>.
+replaced by IPv4 address of eth0 interface (see also B<--netif>).
+Similarly I<$NB_PREFIX> is replaced with prefix given by B<--prefix>.
See also C<uboot> keyword in L</"NOVABOOT SCRIPT SYNTAX">).
Example: C<--expect='Continue?' --sendcont='yes\n'>
+=item --final-eol, --no-final-eol
+
+By default, B<novaboot> always prints an end-of-line character at the
+end of its execution in order to ensure that the output of programs
+started after novaboot appears at the beginning of the line. When this
+is not desired B<--no-final-eol> option can be used to override this
+behavior.
+
=back
=head1 NOVABOOT SCRIPT SYNTAX
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.
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<chld>
+
+Chainload another bootloader. Instead of loading multiboot modules
+identified with C<load> keyword, run another bootloader. This is
+currently supported only by pulsar and can be used to load e.g. Grub
+as in the example below:
+
+ chld boot/grub/i386-pc/core.0
+
+
+=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):
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:
=back
-=head1 CONFIGURATION FILE
+=head1 CONFIGURATION FILES
Novaboot can read its configuration from one or more files. By
default, novaboot looks for files in F</etc/novaboot.d>, file
=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
Michal Sojka <sojka@os.inf.tu-dresden.de>
+Latest novaboot version can be found at
+L<https://github.com/wentasah/novaboot>.
+
=cut
# LocalWords: novaboot Novaboot NOVABOOT TFTP PXE DHCP filename stty