]> rtime.felk.cvut.cz Git - novaboot.git/blobdiff - novaboot
Add an option to disable reseting of the target
[novaboot.git] / novaboot
index e3c78d6d000a4e6a5b21b74ce56a1b42d43973c2..ec60ffe0fe2c9e2b8ee3e160e1eac56465a00dd3 100755 (executable)
--- a/novaboot
+++ b/novaboot
@@ -16,7 +16,7 @@
 use strict;
 use warnings;
 use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ());
-use Getopt::Long qw(GetOptionsFromString);
+use Getopt::Long qw(GetOptionsFromString GetOptionsFromArray);
 use Pod::Usage;
 use File::Basename;
 use File::Spec;
@@ -40,16 +40,16 @@ my $invocation_dir = $ENV{PWD} || getcwd();
 $CFG::hypervisor = "";
 $CFG::hypervisor_params = "serial";
 $CFG::genisoimage = "genisoimage";
-$CFG::qemu = 'qemu -cpu coreduo -smp 2';
+$CFG::qemu = 'qemu-system-i386 -cpu coreduo -smp 2';
 $CFG::default_target = 'qemu';
 %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',
     "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog="  set root=\'(hd0,msdos1)\'"',
-    "ryuglab" => '--server=pc-sojkam.felk.cvut.cz:/srv/tftp --uboot --uboot-init="mw f0000b00 \${psc_cfg}; sleep 1" --remote-cmd="ssh -t pc-sojkam.felk.cvut.cz \"cu -l /dev/ttyUSB0 -s 115200\"" --remote-expect="Connected." --reset-cmd="ssh -t pc-sojkam.felk.cvut.cz \"dtrrts /dev/ttyUSB0 1 1\""',
-    "ryulocal" => '--dhcp-tftp --serial --uboot --uboot-init="dhcp; mw f0000b00 \${psc_cfg}; sleep 1" --reset-cmd="if which dtrrts; then dtrrts $NB_SERIAL 0 1; sleep 0.1; dtrrts $NB_SERIAL 1 1; fi"',
-
+    "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\""',
+    "ryulocal" => '--target ryu --dhcp-tftp --serial --reset-cmd="if which dtrrts; then dtrrts $NB_SERIAL 0 1; sleep 0.1; dtrrts $NB_SERIAL 1 1; fi"',
     );
 chomp(my $nproc = `nproc`);
 $CFG::scons = "scons -j$nproc";
@@ -88,22 +88,31 @@ my @cfgs;
        my @dirs = File::Spec->splitdir($dir);
        $dir = File::Spec->catdir(@dirs[0..$#dirs-1]);
     }
+    @cfgs = reverse @cfgs;
+    $dir = $ENV{'NOVABOOT_CONFIG_DIR'} || '/etc/novaboot.d';
+    if (opendir(my $dh, $dir)) {
+       my @etccfg = map { "$dir/$_" } grep { /^[-_a-zA-Z0-9]+$/ && -f "$dir/$_" } readdir($dh);
+       closedir $dh;
+       @etccfg = sort(@etccfg);
+       @cfgs = ( @etccfg, @cfgs );
+    }
 }
 my $cfg = $ENV{'NOVABOOT_CONFIG'};
 Getopt::Long::Configure(qw/no_ignore_case pass_through/);
 GetOptions ("config|c=s" => \$cfg);
-read_config($_) foreach $cfg or reverse @cfgs;
+read_config($_) foreach $cfg or @cfgs;
 
 ## Command line handling
 
 my $explicit_target;
 GetOptions ("target|t=s" => \$explicit_target);
 
-my (@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, $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_cmd, $rom_prefix, $rsync_flags, @scriptmod, $scons, $serial, $server, $stty, $uboot, $uboot_init);
+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);
 
 $rsync_flags = '';
 $rom_prefix = 'rom://';
 $stty = 'raw -crtscts -onlcr 115200';
+$reset = 1;                    # Reset target by default
 
 my @expect_seen = ();
 sub handle_expect
@@ -123,9 +132,9 @@ sub handle_send
     @expect_seen = ();
 }
 
-Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
 my %opt_spec;
 %opt_spec = (
+    "amt=s"         => \$amt,
     "append|a=s"     => \@append,
     "bender|b"       => \$bender,
     "build-dir=s"    => sub { my ($n, $v) = @_; $builddir = File::Spec->rel2abs($v); },
@@ -141,9 +150,10 @@ my %opt_spec;
     "gen-only"      => \$gen_only,
     "grub|g:s"              => \$grub_config,
     "grub-preamble=s"=> \$grub_preamble,
-    "grub-prefix=s"  => \$grub_prefix,
+    "prefix|grub-prefix=s" => \$grub_prefix,
     "grub2:s"       => \$grub2_config,
     "grub2-prolog=s" => \$grub2_prolog,
+    "ider"           => \$ider,
     "iprelay=s"             => \$iprelay,
     "iso:s"         => \$iso_image,
     "kernel|k=s"     => \$kernel_opt,
@@ -160,6 +170,7 @@ my %opt_spec;
     "qemu-flags|q=s" => \$qemu_flags_cmd,
     "remote-cmd=s"   => \$remote_cmd,
     "remote-expect=s"=> \$remote_expect,
+    "reset!"         => \$reset,
     "reset-cmd=s"    => \$reset_cmd,
     "rsync-flags=s"  => \$rsync_flags,
     "scons:s"       => \$scons,
@@ -170,8 +181,11 @@ my %opt_spec;
     "server:s"              => \$server,
     "strip-rom"             => sub { $rom_prefix = ''; },
     "stty=s"        => \$stty,
-    "uboot"         => \$uboot,
-    "uboot-init=s"   => \$uboot_init,
+    "tftp"          => \$tftp,
+    "tftp-port=i"    => \$tftp_port,
+    "uboot:s"       => \$uboot,
+    "uboot-addr=s"   => \%uboot_addr,
+    "uboot-init=s"   => \@uboot_init,
     "h"             => \$help,
     "help"          => \$man,
     );
@@ -179,10 +193,19 @@ my %opt_spec;
 # First process target options
 {
     my $t = defined($explicit_target) ? $explicit_target : $CFG::default_target;
-    if ($t) {
+    my @target_expanded;
+    Getopt::Long::Configure(qw/no_ignore_case pass_through/);
+    while ($t) {
        exists $CFG::targets{$t} or die("Unknown target '$t' (valid targets are: ".join(", ", sort keys(%CFG::targets)).")");
-       GetOptionsFromString($CFG::targets{$t}, %opt_spec);
+
+       undef $explicit_target;
+       my ($ret, $remaining_args) = GetOptionsFromString ($CFG::targets{$t}, ("target|t=s" => \$explicit_target));
+       if (!$ret) { die "Error parsing target $t option"; }
+       push(@target_expanded, @$remaining_args);
+       $t = $explicit_target;
     }
+    Getopt::Long::Configure(qw/no_ignore_case no_pass_through/);
+    GetOptionsFromArray(\@target_expanded, %opt_spec) or die ("Error in target definition");
 }
 
 # Then process other command line options - some of them may override
@@ -214,6 +237,11 @@ if ($interactive && !-t STDIN) {
 
 if (defined $config_name_opt && scalar(@ARGV) > 1) { die "You cannot use --name with multiple scripts"; }
 
+if ($ider) {
+    $iso_image //= ''; # IDE-R needs an ISO image
+    if (!defined $amt) { die "Error: --ider requires --amt"; }
+}
+
 # Default options
 if (defined $serial) {
     $serial ||= "/dev/ttyUSB0";
@@ -227,8 +255,9 @@ my @scripts;
 my $file;
 my $EOF;
 my $last_fn = '';
-my ($modules, $variables, $generated, $continuation);
-while (<>) {
+my ($modules, $variables, $generated, $continuation) = ([], {}, []);
+my $skip_reading = defined($on_opt) || defined($off_opt);
+while (!$skip_reading && ($_ = <>)) {
     if ($ARGV ne $last_fn) { # New script
        die "Missing EOF in $last_fn" if $file;
        die "Unfinished line in $last_fn" if $continuation;
@@ -256,12 +285,21 @@ while (<>) {
 
     foreach my $mod(@scriptmod) { eval $mod; }
 
+    if ($file && $_ eq $EOF) { # Heredoc end
+       undef $file;
+       next;
+    }
+    if ($file) {               # Heredoc content
+       push @{$file}, "$_\n";
+       next;
+    }
     if (/^([A-Z_]+)=(.*)$/) {  # Internal variable
        $$variables{$1} = $2;
        push(@exiton, $2) if ($1 eq "EXITON");
        next;
     }
     if (s/^load *//) {         # Load line
+       die("novaboot: '$last_fn' line $.: Missing file name\n") unless /^[^ <]+/;
        if (/^([^ ]*)(.*?)[[:space:]]*<<([^ ]*)$/) { # Heredoc start
            push @$modules, "$1$2";
            $file = [];
@@ -277,12 +315,8 @@ while (<>) {
        push @$modules, $_;
        next;
     }
-    if ($file && $_ eq $EOF) { # Heredoc end
-       undef $file;
-       next;
-    }
-    if ($file) {               # Heredoc content
-       push @{$file}, "$_\n";
+    if (/^run (.*)/) {         # run line
+       push @$generated, {command => $1};
        next;
     }
 
@@ -336,7 +370,11 @@ sub generate_configs($$$) {
        print "novaboot: Created $fn\n";
       } elsif (exists $$g{command} && ! $no_file_gen) {
        $ENV{SRCDIR} = dirname(File::Spec->rel2abs( $filename, $invocation_dir ));
-       system_verbose("( $$g{command} ) > $$g{filename}");
+       if (exists $$g{filename}) {
+           system_verbose("( $$g{command} ) > $$g{filename}");
+       } else {
+           system_verbose($$g{command});
+       }
       }
     }
 }
@@ -366,6 +404,39 @@ sub generate_grub_config($$$$;$)
     return $filename;
 }
 
+sub generate_syslinux_config($$$$)
+{
+    my ($filename, $title, $base, $modules_ref) = @_;
+    if ($base && $base !~ /\/$/) { $base = "$base/"; };
+    open(my $fg, '>', $filename) or die "$filename: $!";
+    print $fg "LABEL $title\n";
+    #TODO print $fg "MENU LABEL $human_readable_title\n";
+
+    my ($kbin, $kcmd) = split(' ', @$modules_ref[0], 2);
+
+    if (system("file $kbin|grep 'Linux kernel'") == 0) {
+       my $initrd = @$modules_ref[1];
+       die('To many "load" lines for Linux kernel') if (scalar @$modules_ref > 2);
+       print $fg "LINUX $base$kbin\n";
+       print $fg "APPEND $kcmd\n";
+       print $fg "INITRD $base$initrd\n";
+    } else {
+       print $fg "KERNEL mboot.c32\n";
+       my @append;
+       foreach (@$modules_ref) {
+           s|\brom://([^ ]*)|$rom_prefix$base$1|g; # Translate rom:// files - needed for vdisk parameter of sigma0
+           push @append, "$base$_";
+           print $fg "APPEND ".join(' --- ', @append)."\n";
+       }
+    }
+    #TODO print $fg "TEXT HELP\n";
+    #TODO print $fg "some help here\n";
+    #TODO print $fg "ENDTEXT\n";
+    close($fg);
+    print("novaboot: Created $builddir/$filename\n");
+    return $filename;
+}
+
 sub generate_grub2_config($$$$;$$)
 {
     my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_;
@@ -430,6 +501,7 @@ sub exec_verbose(@)
 {
     print "novaboot: Running: ".shell_cmd_string(@_)."\n";
     exec(@_);
+    exit(1); # should not be reached
 }
 
 sub system_verbose($)
@@ -442,7 +514,7 @@ sub system_verbose($)
     if ($ret) { die("Command failure $ret"); }
 }
 
-## WvTest handline
+## WvTest headline
 
 if (exists $variables->{WVDESC}) {
     print "Testing \"$variables->{WVDESC}\" in $last_fn:\n";
@@ -450,7 +522,7 @@ if (exists $variables->{WVDESC}) {
     print "Testing \"all\" in $last_fn:\n";
 }
 
-## Connect to the target and check whether is not occupied
+## Connect to the target and check whether it is not occupied
 
 # We have to do this before file generation phase, because file
 # generation is intermixed with file deployment phase and we want to
@@ -461,6 +533,7 @@ if (exists $variables->{WVDESC}) {
 my $exp; # Expect object to communicate with the target over serial line
 
 my ($target_reset, $target_power_on, $target_power_off);
+my ($amt_user, $amt_password, $amt_host, $amt_port);
 
 if (defined $iprelay) {
     my $IPRELAY;
@@ -539,10 +612,112 @@ elsif ($serial) {
     system_verbose("stty -F $serial $stty");
     open($CONN, "+<", $serial) || die "open $serial: $!";
     $exp = Expect->init(\*$CONN);
-} elsif ($remote_cmd) {
+}
+elsif ($remote_cmd) {
     print "novaboot: Running: $remote_cmd\n";
     $exp = Expect->spawn($remote_cmd);
 }
+elsif (defined $amt) {
+    require LWP::UserAgent;
+    require LWP::Authen::Digest;
+
+    sub genXML {
+        my ($host, $username, $password, $schema, $className, $pstate) = @_;
+       #AMT numbers for PowerStateChange (MNI => bluescreen on windows;-)
+       my %pstates = ("on"        => 2,
+                      "standby"   => 4,
+                      "hibernate" => 7,
+                      "off"       => 8,
+                      "reset"     => 10,
+                      "MNI"       => 11);
+        return <<END;
+               <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
+               <s:Header><a:To>http://$host:16992/wsman</a:To>
+               <w:ResourceURI s:mustUnderstand="true">$schema</w:ResourceURI>
+               <a:ReplyTo><a:Address s:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>
+               <a:Action s:mustUnderstand="true">$schema$className</a:Action>
+               <w:MaxEnvelopeSize s:mustUnderstand="true">153600</w:MaxEnvelopeSize>
+               <a:MessageID>uuid:709072C9-609C-4B43-B301-075004043C7C</a:MessageID>
+               <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
+               <w:OperationTimeout>PT60.000S</w:OperationTimeout>
+               <w:SelectorSet><w:Selector Name="Name">Intel(r) AMT Power Management Service</w:Selector></w:SelectorSet>
+               </s:Header><s:Body>
+               <p:RequestPowerStateChange_INPUT xmlns:p="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
+               <p:PowerState>$pstates{$pstate}</p:PowerState>
+               <p:ManagedElement><a:Address>http://$host:16992/wsman</a:Address>
+               <a:ReferenceParameters><w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</w:ResourceURI>
+               <w:SelectorSet><w:Selector Name="Name">ManagedSystem</w:Selector></w:SelectorSet>
+               </a:ReferenceParameters></p:ManagedElement>
+               </p:RequestPowerStateChange_INPUT>
+               </s:Body></s:Envelope>
+END
+    }
+
+    sub sendPOST {
+        my ($host, $username, $password, $content) = @_;
+
+        my $ua = LWP::UserAgent->new();
+        $ua->agent("novaboot");
+
+       my $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
+        my $res = $ua->request($req);
+        die ("Unexpected AMT response: " . $res->status_line) unless $res->code == 401;
+
+        my ($realm) = $res->header("WWW-Authenticate") =~ /Digest realm="(.*?)"/;
+        $ua->credentials("$host:16992", $realm, $username => $password);
+
+        # Create a request
+        $req = HTTP::Request->new(POST => "http://$host:16992/wsman");
+        $req->content_type('application/x-www-form-urlencoded');
+        $req->content($content);
+        $res = $ua->request($req);
+        die ("AMT power change request failed: " . $res->status_line) unless $res->is_success;
+        $res->content() =~ /<g:ReturnValue>(\d+)<\/g:ReturnValue>/;
+        return $1;
+    }
+
+    sub powerChange  {
+        my ($host, $username, $password, $pstate)=@_;
+        my $schema="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService";
+        my $className="/RequestPowerStateChange";
+        my $content = genXML($host, $username, $password ,$schema, $className, $pstate);
+        return sendPOST($host, $username, $password, $content);
+    }
+
+    ($amt_user,$amt_password,$amt_host,$amt_port) = ($amt =~ /(?:(.*?)(?::(.*))?@)?([^:]*)(?::([0-9]*))?/);;
+    $amt_user ||= "admin";
+    $amt_password ||= $ENV{'AMT_PASSWORD'} || die "AMT password not specified";
+    $amt_host || die "AMT host not specified";
+    $amt_port ||= 16994;
+
+
+    $target_power_off = sub {
+       $exp->close();
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "off");
+       die "AMT power off failed (ReturnValue $result)" if $result != 0;
+    };
+
+    $target_power_on = sub {
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "on");
+       die "AMT power on failed (ReturnValue $result)" if $result != 0;
+    };
+
+    $target_reset = sub {
+       my $result = powerChange($amt_host,$amt_user,$amt_password, "reset");
+       if ($result != 0) {
+           print STDERR "Warning: Cannot reset $amt_host, trying power on. ";
+           $result = powerChange($amt_host,$amt_user,$amt_password, "on");
+       }
+       die "AMT reset failed (ReturnValue $result)" if $result != 0;
+    };
+
+    my $cmd = "amtterm -u $amt_user -p $amt_password $amt_host $amt_port";
+    print "novaboot: Running: $cmd\n" =~ s/\Q$amt_password\E/???/r;
+    $exp = Expect->spawn($cmd);
+    $exp->expect(10, "RUN_SOL") || die "Expect for 'RUN_SOL' timed out";
+
+}
+
 
 if ($remote_expect) {
     $exp->expect(10, $remote_expect) || die "Expect for '$remote_expect' timed out";
@@ -568,11 +743,14 @@ $builddir ||= dirname(File::Spec->rel2abs( ${$scripts[0]}{filename})) if scalar
 if (defined $builddir) {
     chdir($builddir) or die "Can't change directory to $builddir: $!";
     print "novaboot: Entering directory `$builddir'\n";
+} else {
+    $builddir = $invocation_dir;
 }
 
 ## File generation phase
 my (%files_iso, $menu_iso, $filename);
 my $config_name = '';
+my $prefix = '';
 
 foreach my $script (@scripts) {
     $filename = $$script{filename};
@@ -589,9 +767,11 @@ foreach my $script (@scripts) {
        print "novaboot: Entering directory `$builddir'\n";
     }
 
-    my $prefix;
-    ($prefix = $grub_prefix) =~ s/\$NAME/$config_name/ if defined $grub_prefix;
-    $prefix ||= $builddir;
+    if ($grub_prefix) {
+       $prefix = $grub_prefix;
+       $prefix =~ s/\$NAME/$config_name/;
+       $prefix =~ s/\$BUILDDIR/$builddir/;
+    }
     # TODO: use $grub_prefix as first parameter if some switch is given
     generate_configs('', $generated, $filename);
 
@@ -605,7 +785,7 @@ foreach my $script (@scripts) {
     {
        my @files = map({ ($file) = m/([^ ]*)/; $file; } @$modules);
        # Filter-out generated files
-       my @to_build = grep({ my $file = $_; !scalar(grep($file eq $$_{filename}, @$generated)) } @files);
+       my @to_build = grep({ my $file = $_; !scalar(grep($file eq ($$_{filename} || ''), @$generated)) } @files);
 
        system_verbose($scons || $CFG::scons." ".join(" ", @to_build)) if (defined $scons);
        system_verbose($make  || $CFG::make ." ".join(" ", @to_build)) if (defined $make);
@@ -635,7 +815,7 @@ foreach my $script (@scripts) {
     if (defined $iso_image) {
        generate_configs("(cd)", $generated, $filename);
        my $menu;
-       generate_grub_config(\$menu, $config_name, "(cd)", $modules);
+       generate_syslinux_config(\$menu, $config_name, "/", $modules);
        $menu_iso .= "$menu\n";
        map { ($file,undef) = split; $files_iso{$file} = 1; } @$modules;
     }
@@ -643,12 +823,35 @@ foreach my $script (@scripts) {
 
 ## Generate ISO image
 if (defined $iso_image) {
-    open(my $fh, ">menu-iso.lst");
-    print $fh "timeout 5\n\n$menu_iso";
+    system_verbose("mkdir -p isolinux");
+
+    my @files;
+    if (-f '/usr/lib/ISOLINUX/isolinux.bin') {
+       # Newer ISOLINUX version
+       @files = qw(/usr/lib/ISOLINUX/isolinux.bin /usr/lib/syslinux/modules/bios/mboot.c32 /usr/lib/syslinux/modules/bios/libcom32.c32 /usr/lib/syslinux/modules/bios/menu.c32 /usr/lib/syslinux/modules/bios/ldlinux.c32);
+    } else {
+       # Older ISOLINUX version
+       @files = qw(/usr/lib/syslinux/isolinux.bin /usr/lib/syslinux/mboot.c32 /usr/lib/syslinux/menu.c32);
+    }
+    system_verbose("cp @files isolinux");
+    open(my $fh, ">isolinux/isolinux.cfg");
+    if ($#scripts) {
+       print $fh "TIMEOUT 50\n";
+       print $fh "DEFAULT menu\n";
+    } else {
+       print $fh "DEFAULT $config_name\n";
+    }
+    print $fh "$menu_iso";
     close($fh);
-    my $files = "boot/grub/menu.lst=menu-iso.lst " . join(" ", map("$_=$_", keys(%files_iso)));
+
+    my $files = join(" ", map("$_=$_", (keys(%files_iso), 'isolinux/isolinux.cfg', map(s|.*/|isolinux/|r, @files))));
     $iso_image ||= "$config_name.iso";
-    system_verbose("$CFG::genisoimage -R -b stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -hide-rr-moved -J -joliet-long -o $iso_image -graft-points bin/boot/grub/ $files");
+
+    # Note: We use -U flag below to "Allow 'untranslated' filenames,
+    # completely violating the ISO9660 standards". Without this
+    # option, isolinux is not able to read files names for example
+    # bzImage-3.0.
+    system_verbose("$CFG::genisoimage -R -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -hide-rr-moved -U -o $iso_image -graft-points $files");
     print("ISO image created: $builddir/$iso_image\n");
 }
 
@@ -670,7 +873,7 @@ sub trim($) {
     return $str
 }
 
-### Qemu
+### Start in Qemu
 
 if (defined $qemu) {
     # Qemu
@@ -684,7 +887,7 @@ if (defined $qemu) {
 
     if (defined $iso_image) {
        # Boot NOVA with grub (and test the iso image)
-       push(@qemu_flags, ('-cdrom', "$config_name.iso"));
+       push(@qemu_flags, ('-cdrom', $iso_image));
     } else {
        # Boot NOVA without GRUB
 
@@ -714,6 +917,8 @@ if (defined $qemu) {
 
 my ($dhcpd_pid, $tftpd_pid);
 
+$tftp=1 if $tftp_port;
+
 if (defined $dhcp_tftp)
 {
     generate_configs("(nd)", $generated, $filename);
@@ -741,72 +946,117 @@ host server {
     # process group (e.g. Ctrl-C on terminal).
     $dhcpd_pid = fork();
     exec_verbose("sudo dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid") if ($dhcpd_pid == 0);
-    $tftpd_pid = fork();
-    exec_verbose("sudo in.tftpd --foreground --secure -v -v -v --pidfile tftpd.pid $builddir") if ($tftpd_pid == 0);
+}
+
+if (defined $dhcp_tftp || defined $tftp) {
+    $tftp_port ||= 69;
+    # Unfortunately, tftpd requires root privileges even with
+    # non-privileged (>1023) port due to initgroups().
+    system_verbose("sudo in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid  --address :$tftp_port $builddir");
 
     # Kill server when we die
-    $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid');
+    $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid') if (defined $dhcp_tftp);
                          system_verbose('sudo pkill --pidfile=tftpd.pid'); };
+
+    # We have to kill tftpd explicitely, because it is not in our process group
+    $SIG{INT} = sub { system_verbose('sudo pkill --pidfile=tftpd.pid'); exit(0); };
+}
+
+### AMT IDE-R
+if (defined $ider) {
+    my $ider_cmd= "amtider -c $iso_image -u $amt_user -p $amt_password $amt_host $amt_port"  ;
+    print "novaboot: Running: $ider_cmd\n" =~ s/\Q$amt_password\E/???/r;
+    my $ider_pid = fork();
+    if ($ider_pid == 0) {
+       exec($ider_cmd);
+       die "IDE redirection failed";
+    }
+    # FIXME: This collides with --tftp option. Hopefully, nobody needs
+    # to use both simultaneously.
+    $SIG{__DIE__} = sub { system_verbose('kill $ider_pid'); };
 }
 
-### Serial line or IP relay
+### Reset target (IP relay, AMT, ...)
 
-if (defined $target_reset) {
+if (defined $target_reset && $reset) {
     print "novaboot: Reseting the test box... ";
     &$target_reset();
     print "done\n";
 }
 
+### U-boot conversation
 if (defined $uboot) {
-    print "novaboot: Waiting for uBoot prompt...\n";
+    my $uboot_prompt = $uboot || '=> ';
+    print "novaboot: Waiting for U-Boot prompt...\n";
     $exp->log_stdout(1);
     #$exp->exp_internal(1);
     $exp->expect(20,
                 [qr/Hit any key to stop autoboot:/, sub { $exp->send("\n"); exp_continue; }],
-                '=> ') || die "No uBoot prompt deteceted";
-    $exp->send("$uboot_init\n") if $uboot_init;
-    $exp->expect(10, '=> ') || die "uBoot prompt timeout";
-
-    my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
-    my $dtb;
-    @$modules = map { if (/\.dtb$/) { $dtb=$_; (); } else { $_ } } @$modules;
-    my $initrd = shift @$modules;
-
-    my $kern_addr = '800000';
-    my $initrd_addr = '-';
-    my $dtb_addr = '';
-
-    $exp->send("tftp $kern_addr $kbin\n");
-    $exp->expect(10,
-                [qr/#/, sub { exp_continue; }],
-                '=> ') || die "Kernel load failed";
-    if (defined $dtb) {
-       $dtb_addr = '7f0000';
-       $exp->send("tftp $dtb_addr $dtb\n");
-       $exp->expect(10,
-                    [qr/#/, sub { exp_continue; }],
-                    '=> ') || die "Device tree load failed";
+                $uboot_prompt) || die "No U-Boot prompt deteceted";
+    foreach my $cmd (@uboot_init) {
+       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/;
+           $cmd =~ s/\$NB_MYIP/$ip/g;
+       }
+       chomp($cmd);
+       $exp->send("$cmd\n");
+       $exp->expect(10, $uboot_prompt) || die "U-Boot prompt timeout";
     }
-    if (defined $initrd) {
-       $initrd_addr = 'b00000';
-       $exp->send("tftp $initrd_addr $initrd\n");
+
+    # Boot the system if there are some load lines in the script
+    if (scalar(@$modules) > 0) {
+       my ($kbin, $kcmd) = split(' ', shift(@$modules), 2);
+       my $dtb;
+       @$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,
-                    [qr/#/, sub { exp_continue; }],
-                    '=> ') || die "Initrd load failed";
+                    [qr/##/, sub { exp_continue; }],
+                    $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->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->expect(10,
+                        [qr/#/, sub { exp_continue; }],
+                        $uboot_prompt) || die "Initrd load timeout";
+       }
+       $exp->send("echo $kcmd\n");
+       $exp->expect(1, '-re', qr{echo .*\n(.*)\n$uboot_prompt})  || die "Command line test timeout";
+       my $args = ($exp->matchlist)[0];
+       if ($args =~ /^setenv\s+bootargs/) {
+           $exp->send("$args\n");
+       } else {
+           $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");
+       $exp->expect(5, "\n")  || die "U-Boot command timeout";
     }
-    $exp->send("set bootargs '$kcmd'\n");
-    $exp->expect(5, '=> ')  || die "uBoot prompt timeout";
-    $exp->send("bootm $kern_addr $initrd_addr $dtb_addr\n");
-    $exp->expect(5, "\n")  || die "uBoot command timeout";
 }
 
+### Serial line interaction
 if (defined $exp) {
     # Serial line of the target is available
     my $interrupt = 'Ctrl-C';
     if ($interactive && !@exiton) {
        $interrupt = '"~~."';
     }
-    print "novaboot: Serial line interaction (press $interrupt to interrupt)...\n";
+    my $note = (-t STDIN) ? '' : '- only target->host ';
+    print "novaboot: Serial line interaction $note(press $interrupt to interrupt)...\n";
     $exp->log_stdout(1);
     if (@exiton) {
        $exp->expect(undef, @expect_raw, @exiton);
@@ -834,7 +1084,7 @@ if (defined $exp) {
 }
 
 ## Kill dhcpc or tftpd
-if (defined $dhcp_tftp) {
+if (defined $dhcp_tftp || defined $tftp) {
     die("novaboot: This should kill servers on background\n");
 }
 
@@ -854,16 +1104,20 @@ B<./script> [option]...
 
 =head1 DESCRIPTION
 
-This program makes it easier to boot NOVA or other operating system
-(OS) on different targets (machines or emulators). It reads a so
-called novaboot script, that specifies the boot configuration, and
-setups the target to boot that configuration. Setting up the target
-means to generate the bootloader configuration files, deploy the
-binaries and other needed files to proper locations, perhaps on a
-remote boot server and reset the target. Then, target's serial output
-is redirected to standard output if that is possible.
-
-A typical way of using novaboot is to make the novaboot script
+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.
+
+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.
@@ -877,29 +1131,29 @@ For example, with C<novaboot> you can:
 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 I<script>.
+configuration specified in the F<script>.
 
 =item 2.
 
 Create a bootloader configuration file (currently supported
-bootloaders are GRUB, GRUB2, Pulsar and uBoot) and copy it with all
-other files needed for booting to another, perhaps remote, location.
+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
 
 This command copies files to the TFTP server and uses
-TCP/IP-controlled relay to reset the test box and receive its serial
-output.
+TCP/IP-controlled relay to reset the target host and receive its
+serial output.
 
 =item 3.
 
-Run DHCP and TFTP server on developer's machine to PXE-boot the OS
-from it. E.g.
+Run DHCP and TFTP server on developer's machine to PXE-boot the target
+machine from it. E.g.
 
  ./script --dhcp-tftp
 
 When a PXE-bootable machine is connected via Ethernet to developer's
-machine, it will boot the configuration described in I<script>.
+machine, it will boot the configuration described in the I<script>.
 
 =item 4.
 
@@ -907,38 +1161,52 @@ Create bootable ISO images. E.g.
 
  novaboot --iso -- script1 script2
 
-The created ISO image will have GRUB bootloader installed on it and
+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.
 
 =back
 
 Note that the options needed for a specific target can be stored in a
-L</"CONFIGURATION FILE"> and then it is sufficient to use only the
-B<-t> option to specify the name of the target.
+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 options, certain phases can be skipped. The list
-of phases (in the execution order) and the corresponding options
-follow.
+influenced by several command line options, certain phases can be
+skipped. The list of phases (in the execution order) and the
+corresponding options follow.
 
 =head2 Configuration reading phase
 
-After starting, novaboot reads configuration files. By default, it
-searches for files named F<.novaboot> starting from the directory of
-the novaboot script (or working directory, see bellow) and continuing
-upwards up to the root directory. The configuration files are read in
-order from the root directory downwards with latter files overriding
-settings from the former ones.
+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
+former ones.
+
+Configuration directory is determined by the content of
+NOVABOOT_CONFIG_DIR environment variable defaulting to
+F</etc/novaboot.d>. Files in this directory with names consisting
+solely from 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
+directory of the novaboot script (or working directory, see bellow)
+and continuing upwards up to the root directory. The found
+configuration files are then read in the opposite order (i.e. from the
+root directory downwards). This allows to have, for example, user
+specific configuration in F<~/.novaboot> and project specific one in
+F<~/project/.novaboot>.
 
 In certain cases, the location of the novaboot script cannot be
 determined in this early phase. This happens either when the script is
-read from the standard input or when novaboot is invoked explicitly
-and options precede the script name, as in the example L</"4."> above.
-In this case the current working directory is used as a starting point
-for configuration file search.
+read from the standard input or when novaboot is invoked explicitly as
+in the example L</"4."> above. In this case the current working
+directory is used as a starting point for configuration file search
+instead of the novaboot script directory.
 
 =over 8
 
@@ -981,7 +1249,7 @@ used in the later phases.
 
 Append a string to the first C<load> line in the novaboot script. This
 can be used to append parameters to the kernel's or root task's
-command line. Can appear multiple times.
+command line. This option can appear multiple times.
 
 =item -b, --bender
 
@@ -991,8 +1259,9 @@ use by the kernel.
 
 =item --chainloader=I<chainloader>
 
-Chainloader that is loaded before the kernel and other files specified
-in the novaboot script. E.g. 'bin/boot/bender promisc'.
+Specifies a chainloader that is loaded before the kernel and other
+files specified in the novaboot script. E.g. 'bin/boot/bender
+promisc'.
 
 =item --dump
 
@@ -1022,7 +1291,7 @@ line order.
 =head2 File generation phase
 
 In this phase, files needed for booting are generated in a so called
-I<build directory> (see L<--build-dir>). In most cases configuration
+I<build directory> (see L</--build-dir>). In most cases configuration
 for a bootloader is generated automatically by novaboot. It is also
 possible to generate other files using I<heredoc> or I<"<"> syntax in
 novaboot scripts. Finally, binaries can be generated in this phases by
@@ -1039,7 +1308,7 @@ configuration file defines the C<$builddir> variable, its value is
 used. Otherwise, it is the directory that contains the first processed
 novaboot script.
 
-See also L<BUILDDIR> variable.
+See also L</BUILDDIR> variable.
 
 =item -g, --grub[=I<filename>]
 
@@ -1053,22 +1322,30 @@ Specifies the I<preable> that is at the beginning of the generated
 GRUB or GRUB2 config files. This is useful for specifying GRUB's
 timeout.
 
-=item --grub-prefix=I<prefix>
+=item --prefix=I<prefix>
 
-Specifies I<prefix> that is put in front of every file name in GRUB's
-F<menu.lst>. The default value is the absolute path to the build directory.
+Specifies I<prefix> (e.g. F</srv/tftp>) that is put in front of every
+file name in generated bootloader configuration files (or in U-Boot
+commands).
 
 If the I<prefix> contains string $NAME, it will be replaced with the
 name of the novaboot script (see also B<--name>).
 
+If the I<prefix> contains string $BUILDDIR, it will be replaced with
+the build directory (see also B<--build-dir>).
+
+=item --grub-prefix
+
+Alias for B<--prefix>.
+
 =item --grub2[=I<filename>]
 
-Generate GRUB2 menuentry in I<filename>. If I<filename> is not
-specified F<grub.cfg> is used. The content of the menuentry can be
-customized with B<--grub-preable>, B<--grub2-prolog> or
+Generate GRUB2 menu entry in I<filename>. If I<filename> is not
+specified F<grub.cfg> is used. The content of the menu entry can be
+customized with B<--grub-preamble>, B<--grub2-prolog> or
 B<--grub_prefix> options.
 
-In order to use the the generated menuentry on your development
+In order to use the the generated menu entry on your development
 machine that uses GRUB2, append the following snippet to
 F</etc/grub.d/40_custom> file and regenerate your grub configuration,
 i.e. run update-grub on Debian/Ubuntu.
@@ -1079,8 +1356,7 @@ i.e. run update-grub on Debian/Ubuntu.
 
 =item --grub2-prolog=I<prolog>
 
-Specifies text I<preable> that is put at the beginning of the entry
-GRUB2 entry.
+Specifies text that is put at the beginning of the GRUB2 menu entry.
 
 =item -m, --make[=make command]
 
@@ -1094,8 +1370,9 @@ server directory where the boot files are copied to.
 
 =item --no-file-gen
 
-Do not generate files on the fly (i.e. "<" syntax) except for the
-files generated via "<<WORD" syntax.
+Do not run external commands to generate files (i.e. "<" syntax and
+C<run> keyword). This switch does not influence generation of files
+specified with "<<WORD" syntax.
 
 =item -p, --pulsar[=mac]
 
@@ -1127,6 +1404,15 @@ user/instance.
 
 =over 8
 
+=item --amt=I<"[user[:password]@]host[:port]>
+
+Use Intel AMT technology to control the target machine. WS management
+is used to powercycle it and Serial-Over-Lan (SOL) for input/output.
+The hostname or (IP address) is given by the I<host> parameter. If
+I<password> is not specified, environment variable AMT_PASSWORD is
+used. The I<port> specifies a TCP port for SOL. If not specified, the
+default is 16992. Default I<user> is admin.
+
 =item --iprelay=I<addr[:port]>
 
 Use TCP/IP relay and serial port to access the target's serial port
@@ -1159,6 +1445,7 @@ example C<ssh server 'cu -l /dev/ttyS0'>.
 Wait for reception of I<string> after establishing the the remote
 connection before continuing.
 
+
 =back
 
 =head2 File deployment phase
@@ -1175,14 +1462,30 @@ Turns your workstation into a DHCP and TFTP server so that the OS can
 be booted via PXE BIOS (or similar mechanism) on the test machine
 directly connected by a plain Ethernet cable to your workstation.
 
-The DHCP and TFTP servers require root privileges and C<novaboot>
+The DHCP and TFTP servers requires root privileges and C<novaboot>
 uses C<sudo> command to obtain those. You can put the following to
-I</etc/sudoers> to allow running the necessary commands without
-asking for password.
+I</etc/sudoers> to allow running the necessary commands without asking
+for password.
 
- Cmnd_Alias NOVABOOT = /bin/ip a add 10.23.23.1/24 dev eth0, /bin/ip l set dev eth0 up, /usr/sbin/dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid, /usr/sbin/in.tftpd --foreground --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/touch dhcpd.leases, /usr/bin/pkill --pidfile=dhcpd.pid, /usr/bin/pkill --pidfile=tftpd.pid
+ Cmnd_Alias NOVABOOT = /bin/ip a add 10.23.23.1/24 dev eth0, /bin/ip l set dev eth0 up, /usr/sbin/dhcpd -d -cf dhcpd.conf -lf dhcpd.leases -pf dhcpd.pid, /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/touch dhcpd.leases, /usr/bin/pkill --pidfile=dhcpd.pid, /usr/bin/pkill --pidfile=tftpd.pid
  your_login ALL=NOPASSWD: NOVABOOT
 
+=item --tftp
+
+Starts a TFTP server on your workstation. This is similar to
+B<--dhcp-tftp> except that DHCP server is not started.
+
+The TFTP server require root privileges and C<novaboot> uses C<sudo>
+command to obtain those. You can put the following to I</etc/sudoers>
+to allow running the necessary commands without asking for password.
+
+ Cmnd_Alias NOVABOOT =  /usr/sbin/in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid *, /usr/bin/pkill --pidfile=tftpd.pid
+ your_login ALL=NOPASSWD: NOVABOOT
+
+=item --tftp-port=I<port>
+
+Port to run the TFTP server on. Implies B<--tftp>.
+
 =item --iso[=filename]
 
 Generates the ISO image that boots NOVA system via GRUB. If no filename
@@ -1191,11 +1494,15 @@ of the novaboot script (see also B<--name>).
 
 =item --server[=[[user@]server:]path]
 
-Copy all files needed for booting to another location (implies B<-g>
-unless B<--grub2> is given). The files will be copied (by B<rsync>
-tool) to the directory I<path>. If the I<path> contains string $NAME,
-it will be replaced with the name of the novaboot script (see also
-B<--name>).
+Copy all files needed for booting to another location. The files will
+be copied (by B<rsync> tool) to the directory I<path>. If the I<path>
+contains string $NAME, it will be replaced with the name of the
+novaboot script (see also B<--name>).
+
+=item --rsync-flags=I<flags>
+
+Specifies which I<flags> are appended to F<rsync> command line when
+copying files as a result of I<--server> option.
 
 =item --concat
 
@@ -1206,21 +1513,30 @@ with $NAME part removed. The content of the file is created by
 concatenating all files of the same name from all subdirectories of
 I<path-wo-name> found on the "server".
 
-=item --rsync-flags=I<flags>
+=item --ider
 
-Specifies which I<flags> are appended to F<rsync> command line when
-copying files as a result of I<--server> option.
+Use Intel AMT technology for IDE redirection. This allows the target
+machine to boot from novaboot created ISO image. Implies B<--iso>.
+
+The experimental C<amtider> utility needed by this option can be
+obtained from https://github.com/wentasah/amtterm.
 
 =back
 
 =head2 Target power-on and reset phase
 
+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>.
+
 =over 8
 
 =item --on, --off
 
-Switch on/off the target machine. Currently works only with
-B<--iprelay>.
+Switch on/off the target machine and exit. The script (if any) is
+completely ignored. Currently it works only with B<--iprelay> or
+B<--amt>.
 
 =item -Q, --qemu[=I<qemu-binary>]
 
@@ -1241,23 +1557,38 @@ Replace the default qemu flags (QEMU_FLAGS variable or C<-cpu coreduo
 
 Command that resets the target.
 
+=item --no-reset, --reset
+
+Disable/enable reseting of the target.
+
 =back
 
 =head2 Interaction with the bootloader on the target
 
 =over 8
 
-=item --uboot
+=item --uboot[=I<prompt>]
 
-Interact with uBoot bootloader to boot the thing described in the
-novaboot script. Implementation of this option is currently tied to a
-particular board that we use. It may be subject to changes in the
-future!
+Interact with U-Boot bootloader to boot the thing described in the
+novaboot script. I<prompt> specifies the U-Boot's prompt (default is
+"=> ", other common prompts are "U-Boot> " or "U-Boot# ").
+Implementation of this option is currently tied to a particular board
+that we use. It may be subject to changes in the future!
 
 =item --uboot-init
 
 Command(s) to send the U-Boot bootloader before loading the images and
-booting them.
+booting them. This option can be given multiple times. After sending
+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.
+
+=item --uboot-addr I<name>=I<address>
+
+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).
 
 =back
 
@@ -1333,7 +1664,7 @@ Lines that end with "\" are concatenated with the following line after
 removal of the final "\" and leading whitespace of the following line.
 
 Lines of the form I<VARIABLE=...> (i.e. matching '^[A-Z_]+=' regular
-expression) assign values to internal variables. See L<VARIABLES>
+expression) assign values to internal variables. See L</VARIABLES>
 section.
 
 Lines starting with C<load> keyword represent modules to boot. The
@@ -1346,16 +1677,30 @@ until the line containing solely WORD are copied literally to the file
 named on that line. This is similar to shell's heredoc feature.
 
 When the C<load> line ends with "< CMD" then command CMD is executed
-with C</bin/sh> and its standard output is stored in the file named on
+with F</bin/sh> and its standard output is stored in the file named on
 that line. The SRCDIR variable in CMD's environment is set to the
 absolute path of the directory containing the interpreted novaboot
 script.
 
-Example:
+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.
+
+Example (Linux):
+
+  #!/usr/bin/env novaboot
+  load bzImage console=ttyS0,115200
+  run  make -C buildroot
+  load rootfs.cpio < gen_cpio buildroot/images/rootfs.cpio "myapp->/etc/init.d/S99myapp"
+
+Example (NOVA User Land - NUL):
+
   #!/usr/bin/env novaboot
   WVDESC=Example program
   load bin/apps/sigma0.nul S0_DEFAULT script_start:1,1 \
-    verbose hostkeyb:0,0x60,1,12,2
+                           verbose hostkeyb:0,0x60,1,12,2
   load bin/apps/hello.nul
   load hello.nulconfig <<EOF
   sigma0::mem:16 name::/s0/log name::/s0/timer name::/s0/fs/rom ||
@@ -1410,7 +1755,7 @@ Use specific qemu flags (can be overridden with B<-q>).
 
 =item WVDESC
 
-Description of the wvtest-compliant program.
+Description of the WvTest-compliant program.
 
 =item WVTEST_TIMEOUT
 
@@ -1425,13 +1770,13 @@ intermediate output.
 
 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, its 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.
+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.
 
 Supported configuration variables include:
 
@@ -1449,10 +1794,11 @@ specified on command line with the B<--target> option.
 
 =item %targets
 
-Hash of shortcuts to be used with the B<--target> option. If the hash
-contains, for instance, the following pair of values
+Hash of target definitions to be used with the B<--target> option. The
+key is the identifier of the target, the value is the string with
+command line options. For instance, if the configuration file contains:
 
'mybox' => '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
$targets{'mybox'} = '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub',
 
 then the following two commands are equivalent:
 
@@ -1475,6 +1821,11 @@ override the environment variables.
 Name of the novaboot configuration file to use instead of the default
 one(s).
 
+=item NOVABOOT_CONFIG_DIR
+
+Name of the novaboot configuration directory. When not specified
+F</etc/novaboot.d> is used.
+
 =item NOVABOOT_BENDER
 
 Defining this variable has the same meaning as B<--bender> option.
@@ -1484,3 +1835,10 @@ Defining this variable has the same meaning as B<--bender> option.
 =head1 AUTHORS
 
 Michal Sojka <sojka@os.inf.tu-dresden.de>
+
+=cut
+
+# LocalWords:  novaboot Novaboot NOVABOOT TFTP PXE DHCP filename stty
+# LocalWords:  chainloader stdout Qemu qemu preprocessing ISOLINUX bootable
+# LocalWords:  config subprocesses sudo sudoers tftp dhcp IDE stdin
+# LocalWords:  subdirectories TTY whitespace heredoc POSIX WvTest