]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
uboot: Add default addresses for --uboot-addr
[novaboot.git] / novaboot
index d0d097d9fb05294d5a157de2ed9a698ffd5e02e4..42743a77489c09cd67d6b842663fd59e770a8bee 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -61,6 +61,7 @@ sub read_config($) {
     my ($cfg) = @_;
     {
        package CFG; # Put config data into a separate namespace
+
        my $rc = do($cfg);
 
        # Check for errors
@@ -107,7 +108,13 @@ read_config($_) foreach $cfg or @cfgs;
 my $explicit_target;
 GetOptions ("target|t=s" => \$explicit_target);
 
-my ($amt, @append, $bender, @chainloaders, $concat, $config_name_opt, $dhcp_tftp, $dump_opt, $dump_config, @exiton, @expect_raw, $gen_only, $grub_config, $grub_prefix, $grub_preamble, $grub2_prolog, $grub2_config, $help, $ider, $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, $reset, $reset_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, @uboot_init);
+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, $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, $reset, $reset_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $tftp, $tftp_port, $uboot, %uboot_addr, $uboot_cmd, @uboot_init);
+
+%uboot_addr = (
+    'kernel'  => '${kernel_addr_r}',
+    'ramdisk' => '${ramdisk_addr_r}',
+    'fdt'     => '${fdt_addr_r}',
+    );
 
 $rsync_flags = '';
 $rom_prefix = 'rom://';
@@ -144,6 +151,8 @@ my %opt_spec;
     "dump"          => \$dump_opt,
     "dump-config"    => \$dump_config,
     "exiton=s"       => \@exiton,
+    "exiton-timeout=i"=> \$exiton_timeout,
+    "exiton-re=s"    => sub { my ($n, $v) = @_; push(@exiton, '-re', $v); },
     "expect=s"      => \&handle_expect,
     "expect-re=s"    => \&handle_expect,
     "expect-raw=s"   => sub { my ($n, $v) = @_; unshift(@expect_raw, eval($v)); },
@@ -185,6 +194,7 @@ my %opt_spec;
     "tftp-port=i"    => \$tftp_port,
     "uboot:s"       => \$uboot,
     "uboot-addr=s"   => \%uboot_addr,
+    "uboot-cmd=s"    => \$uboot_cmd,
     "uboot-init=s"   => \@uboot_init,
     "h"             => \$help,
     "help"          => \$man,
@@ -319,10 +329,15 @@ while (!$skip_reading && ($_ = <>)) {
        push @$generated, {command => $1};
        next;
     }
-    if (/^uboot (.*)/) {       # uboot line
-       # TODO: If U-Boot supports some interactive menud, it might
+    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.
-       push @uboot_init, $1;
+       if ($1) {               # Command with explicit timeout
+           push @uboot_init, { command => $2,
+                               timeout => $1 };
+       } else {                # Command without explicit timeout
+           push @uboot_init, $2;
+       }
        next;
     }
 
@@ -726,7 +741,8 @@ END
 
 
 if ($remote_expect) {
-    $exp->expect(10, $remote_expect) || die "Expect for '$remote_expect' timed out";
+    $exp || die("No serial line connection");
+    $exp->expect(180, $remote_expect) || die "Expect for '$remote_expect' timed out";
 }
 
 if (defined $reset_cmd) {
@@ -994,12 +1010,21 @@ if (defined $target_reset && $reset) {
 if (defined $uboot) {
     my $uboot_prompt = $uboot || '=> ';
     print "novaboot: Waiting for U-Boot prompt...\n";
+    $exp || die("No serial line connection");
     $exp->log_stdout(1);
     #$exp->exp_internal(1);
     $exp->expect(20,
                 [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
                 $uboot_prompt) || die "No U-Boot prompt deteceted";
-    foreach my $cmd (@uboot_init) {
+    foreach my $cmdspec (@uboot_init) {
+       my ($cmd, $timeout);
+       if (ref($cmdspec) eq "HASH") {
+           $cmd = $cmdspec->{command};
+           $timeout = $cmdspec->{timeout};
+       } else {
+           $cmd = $cmdspec;
+           $timeout = 10;
+       }
        if ($cmd =~ /\$NB_MYIP/) {
            my $ip = (grep /inet /, `ip addr show eth0`)[0] || die "Problem determining our IP address";
            $ip =~ s/\s*inet ([0-9.]*).*/$1/;
@@ -1012,7 +1037,7 @@ if (defined $uboot) {
        }
        chomp($cmd);
        $exp->send("$cmd\n");
-       $exp->expect(10, $uboot_prompt) || die "U-Boot prompt timeout";
+       $exp->expect($timeout, $uboot_prompt) || die "U-Boot prompt timeout";
     }
 
     # Boot the system if there are some load lines in the script
@@ -1022,8 +1047,6 @@ if (defined $uboot) {
        @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
        my $initrd = shift @$modules;
 
-       my ($ramdisk_addr, $fdt_addr) = ('-', '');
-
        die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel};
        $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n");
        $exp->expect(10,
@@ -1031,19 +1054,19 @@ if (defined $uboot) {
                     $uboot_prompt) || die "Kernel load timeout";
        if (defined $dtb) {
            die "No '--uboot-addr fdt' given" unless $uboot_addr{fdt};
-           $fdt_addr = $uboot_addr{fdt};
-           $exp->send("tftpboot $fdt_addr $prefix$dtb\n");
+           $exp->send("tftpboot $uboot_addr{fdt} $prefix$dtb\n");
            $exp->expect(10,
                         [qr/##/, sub { exp_continue; }],
                         $uboot_prompt) || die "Device tree load timeout";
        }
        if (defined $initrd) {
            die "No '--uboot-addr ramdisk' given" unless $uboot_addr{ramdisk};
-           $ramdisk_addr = $uboot_addr{ramdisk};
-           $exp->send("tftpboot $ramdisk_addr $prefix$initrd\n");
+           $exp->send("tftpboot $uboot_addr{ramdisk} $prefix$initrd\n");
            $exp->expect(10,
-                        [qr/#/, sub { exp_continue; }],
+                        [qr/##/, sub { exp_continue; }],
                         $uboot_prompt) || die "Initrd load timeout";
+       } else {
+           $uboot_addr{ramdisk} = '-';
        }
        $exp->send("echo $kcmd\n");
        $exp->expect(1, '-re', qr{echo .*\n(.*)\n$uboot_prompt})  || die "Command line test timeout";
@@ -1054,7 +1077,13 @@ if (defined $uboot) {
            $exp->send("setenv bootargs $kcmd\n");
        }
        $exp->expect(5, $uboot_prompt)  || die "U-Boot prompt timeout";
-       $exp->send("bootm $uboot_addr{kernel} $ramdisk_addr $fdt_addr\n");
+
+       $uboot_cmd //= $variables->{UBOOT_CMD} // 'bootm $kernel_addr $ramdisk_addr $fdt_addr';
+       $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;
+
+       $exp->send($uboot_cmd . "\n");
        $exp->expect(5, "\n")  || die "U-Boot command timeout";
     }
 }
@@ -1070,7 +1099,7 @@ if (defined $exp) {
     print "novaboot: Serial line interaction $note(press $interrupt to interrupt)...\n";
     $exp->log_stdout(1);
     if (@exiton) {
-       $exp->expect(undef, @expect_raw, @exiton);
+       $exp->expect($exiton_timeout, @expect_raw, @exiton) || die("exiton timeout");
     } else {
        my @inputs = ($exp);
        if (-t STDIN) { # Set up bi-directional communication if we run on terminal
@@ -1103,7 +1132,8 @@ if (defined $dhcp_tftp || defined $tftp) {
 
 =head1 NAME
 
-novaboot - A tool for booting various operating systems on various hardware or in qemu
+novaboot - Boots a locally compiled operating system on a remote
+target or in qemu
 
 =head1 SYNOPSIS
 
@@ -1115,79 +1145,79 @@ B<./script> [option]...
 
 =head1 DESCRIPTION
 
-This program makes booting of an operating system (e.g. NOVA or Linux)
-as simple as running a local program. It facilitates booting on local
-or remote hosts or in emulators such as qemu. 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">). Based on this input,
-novaboot setups everything for the target host to boot the desired
-configuration, i.e. it generates the bootloader configuration file in
-the proper format, deploy the binaries and other needed files to
-required locations, perhaps on a remote boot server and reset the
-target host. Finally, target host's serial output is redirected to
-standard output if that is possible.
-
+This program 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
+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.
 
-For example, with C<novaboot> you can:
+Novaboot uses configuration files to, among other things, define
+command line options needed for different targets. Users typically use
+only the B<-t>/B<--target> command line option to select the target.
+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">.
+
+Simple examples of using C<novaboot>:
 
 =over 3
 
 =item 1.
 
 Run an OS in Qemu. This is the default action when no other action is
-specified by command line switches. Thus running C<novaboot ./script>
-(or C<./script> as described above) will run Qemu and make it boot the
-configuration specified in the F<script>.
+specified by command line switches. Thus running C<novaboot myos> (or
+C<./myos> as described above) will run Qemu and make it boot the
+configuration specified in the F<myos> script.
 
 =item 2.
 
 Create a bootloader configuration file (currently supported
 bootloaders are GRUB, GRUB2, ISOLINUX, Pulsar and U-Boot) and copy it
-with all other files needed for booting to a remote boot server.
-
- ./script --server=192.168.1.1:/tftp --iprelay=192.168.1.2
+with all other files needed for booting to a remote boot server. Then
+use a TCP/IP-controlled relay/serial-to-TCP converter to reset the
+target and receive its serial output.
 
-This command copies files to the TFTP server and uses
-TCP/IP-controlled relay to reset the target host and receive its
-serial output.
+ ./myos --grub2 --server=192.168.1.1:/tftp --iprelay=192.168.1.2
 
 =item 3.
 
-Run DHCP and TFTP server on developer's machine to PXE-boot the target
-machine from it. E.g.
+Run DHCP and TFTP server on developer's machine to boot the target
+from it.
 
- ./script --dhcp-tftp
+ ./myos --dhcp-tftp
 
-When a PXE-bootable machine is connected via Ethernet to developer's
-machine, it will boot the configuration described in the I<script>.
+This is useful when no network infrastructure is in place and
+the target is connected directly to developer's box.
 
 =item 4.
 
-Create bootable ISO images. E.g.
+Create bootable ISO image.
 
  novaboot --iso -- script1 script2
 
-The created ISO image will use ISOLINUX bootloader installed on it and
-the boot menu will allow selecting between I<script1> and I<script2>
-configurations.
+The created ISO image will have ISOLINUX bootloader installed on it
+and the boot menu will allow selecting between I<script1> and
+I<script2> configurations.
 
 =back
 
-Note that the options needed for a specific target can be stored in a
-L</"CONFIGURATION FILE">. Then it is sufficient to use only the B<-t>
-option to specify the name of the target.
-
 =head1 PHASES AND OPTIONS
 
 Novaboot performs its work in several phases. Each phase can be
 influenced by several command line options, certain phases can be
 skipped. The list of phases (in the execution order) and the
-corresponding options follow.
+corresponding options follows.
 
 =head2 Configuration reading phase
 
@@ -1195,13 +1225,13 @@ After starting, novaboot reads configuration files. Their content is
 described in section L</"CONFIGURATION FILE">. By default,
 configuration is read from two locations. First from the configuration
 directory and second from F<.novaboot> files along the path to the
-current directory. The later read files override settings from the
+current directory. The latter read files override settings from the
 former ones.
 
 Configuration directory is determined by the content of
-NOVABOOT_CONFIG_DIR environment variable defaulting to
+NOVABOOT_CONFIG_DIR environment variable and defaults to
 F</etc/novaboot.d>. Files in this directory with names consisting
-solely from English letters, numbers, dashes '-' and underscores '_'
+solely of English letters, numbers, dashes '-' and underscores '_'
 (note that dot '.' is not included) are read in alphabetical order.
 
 Then novaboot searches for files named F<.novaboot> starting from the
@@ -1233,7 +1263,7 @@ Use the specified configuration file instead of the default one(s).
 
 =item --dump-config
 
-Dump the current configuration to stdout end exits. Useful as an
+Dump the current configuration to stdout end exit. Useful as an
 initial template for a configuration file.
 
 =item -h, --help
@@ -1570,7 +1600,7 @@ Command that resets the target.
 
 =item --no-reset, --reset
 
-Disable/enable reseting of the target.
+Disable/enable resetting of the target.
 
 =back
 
@@ -1604,6 +1634,21 @@ Load address of U-Boot's C<tftpboot> command for loading I<name>,
 where name is one of I<kernel>, I<ramdisk> or I<fdt> (flattened device
 tree).
 
+The default addresses are ${I<name>_addr_r}, i.e. U-Boot environment
+variables used by convention for this purpose.
+
+=item --uboot-cmd=I<command>
+
+Specifies U-Boot command used to execute the OS. If the command
+contains strings C<$kernel_addr>, C<$ramdisk_addr>, C<$fdt_addr>,
+these are replaced with the addresses configured with B<--uboot-addr>.
+
+The default value is
+
+    bootm $kernel_addr $ramdisk_addr $fdt_addr
+
+or the C<UBOOT_CMD> variable if defined in the novaboot script.
+
 =back
 
 =head2 Target interaction phase
@@ -1617,13 +1662,24 @@ interactive work with the target.
 =item --exiton=I<string>
 
 When I<string> is sent by the target, novaboot exits. This option can
-be specified multiple times.
+be specified multiple times, in which case novaboot exits whenever
+either of the specified strings is sent.
 
 If I<string> is C<-re>, then the next B<--exiton>'s I<string> is
 treated as regular expression. For example:
 
     --exiton -re --exiton 'error:.*failed'
 
+=item --exiton-re=I<regex>
+
+The same as --exiton -re --exiton I<regex>.
+
+=item --exiton-timeout=I<seconds>
+
+By default B<--exiton> waits for the string match forever. When this
+option is specified, "exiton" timeouts after the specifies number of
+seconds and novaboot returns non-zero exit code.
+
 =item -i, --interactive
 
 Setup things for interactive use of target. Your terminal will be
@@ -1705,7 +1761,11 @@ 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.
+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.
 
 Example (Linux):
 
@@ -1779,6 +1839,10 @@ QEMU_FLAGS.
 
 Use specific qemu flags (can be overridden with B<-q>).
 
+=item UBOOT_CMD
+
+See L</--uboot-cmd>.
+
 =item WVDESC
 
 Description of the WvTest-compliant program.
@@ -1795,14 +1859,15 @@ intermediate output.
 =head1 CONFIGURATION FILE
 
 Novaboot can read its configuration from one or more files. By
-default, novaboot looks for files named F<.novaboot> as described in
-L</Configuration reading phase>. Alternatively, configuration file
-location can be specified with the B<-c> switch or with the
-NOVABOOT_CONFIG environment variable. The configuration file has Perl
-syntax and should set values of certain Perl variables. The current
-configuration can be dumped with the B<--dump-config> switch. Some
-configuration variables can be overridden by environment variables
-(see below) or by command line switches.
+default, novaboot looks for files in F</etc/novaboot.d> and files
+named F<.novaboot> as described in L</Configuration reading phase>.
+Alternatively, configuration file location can be specified with the
+B<-c> switch or with the NOVABOOT_CONFIG environment variable. The
+configuration file has Perl syntax (i.e. it is better to put C<1;> as
+a last line) and should set values of certain Perl variables. The
+current configuration can be dumped with the B<--dump-config> switch.
+Some configuration variables can be overridden by environment
+variables (see below) or by command line switches.
 
 Supported configuration variables include:
 
@@ -1828,8 +1893,8 @@ command line options. For instance, if the configuration file contains:
 
 then the following two commands are equivalent:
 
- ./script --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub
- ./script -t mybox
+ ./myos --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub
+ ./myos -t mybox
 
 =back