%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\""',
"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, $copy, $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
'variables' => $variables = {},
'generated' => $generated = [],
'copy' => $copy = [],
+ 'chainload' => $chainload = [],
};
}
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;
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);
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: $!");
$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 IP address of $netif";
$ip =~ s/\s*inet ([0-9.]*).*/$1/;
$before_match, $after_match) =
$exp->expect($timeout, $uboot_prompt);
die "No U-Boot prompt: $error" if $error;
- #print "\n>>>>$before_match<<<<\n";
+
+ 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
#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;
}
}
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
=item C<uboot>
-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.
+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
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: