X-Git-Url: http://rtime.felk.cvut.cz/gitweb/novaboot.git/blobdiff_plain/57c66bfdf0e73d1ad65c039571830d96a559050f..HEAD:/novaboot diff --git a/novaboot b/novaboot index 5941b31..cd59748 100755 --- a/novaboot +++ b/novaboot @@ -17,11 +17,14 @@ use strict; use warnings; -use warnings (exists $ENV{NOVABOOT_TEST} ? (FATAL => 'all') : ()); +use warnings (exists $ENV{NOVABOOT_TEST} ? + (FATAL => 'all') : + (FATAL => qw(inplace))); # Open warnings in <<>> are fatal use Getopt::Long qw(GetOptionsFromString GetOptionsFromArray); use Pod::Usage; use File::Basename; use File::Spec; +use File::Path qw(make_path); use IO::Handle; use Time::HiRes("usleep"); use Socket; @@ -55,9 +58,9 @@ $CFG::default_target = ''; $CFG::netif = 'eth0'; %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', + "tud" => '--copy=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" => '--ssh=novabox@rtime.felk.cvut.cz', - "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --server=/boot/novaboot/$NAME --grub2 --grub-prefix=/boot/novaboot/$NAME --grub2-prolog=" set root=\'(hd0,msdos1)\'"', + "localhost" => '--scriptmod=s/console=tty[A-Z0-9,]+// --copy=/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 --ssh=ryu@pc-sojkam.felk.cvut.cz', "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"', @@ -233,6 +236,7 @@ my %opt_spec = ( "sendcont=s" => \&handle_send, "serial|s:s" => \$serial, "server:s" => \$server, + "copy:s" => \$server, "ssh:s" => \&handle_novaboot_server, "strip-rom" => sub { $rom_prefix = ''; }, "stty=s" => \$stty, @@ -346,7 +350,7 @@ if (defined $serial) { $ENV{NB_SERIAL} = $serial; } if (defined $grub_config) { $grub_config ||= "menu.lst"; } -if (defined $grub2_config) { $grub2_config ||= "grub.cfg"; } +if (defined $grub2_config) { $grub2_config ||= "./boot/grub/grub.cfg"; } ## Parse the novaboot script(s) my @scripts; @@ -355,7 +359,7 @@ my $EOF; my $last_fn = ''; my ($modules, $variables, $generated, $copy, $chainload, $continuation) = ([], {}, [], []); my $skip_reading = defined($on_opt) || defined($off_opt); -while (!$skip_reading && ($_ = <>)) { +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; @@ -412,6 +416,7 @@ while (!$skip_reading && ($_ = <>)) { push @$generated, {filename => $1, command => $3}; return "$1$2"; } + s/\s*$//; # Strip trailing whitespace return $_; } if (s/^load *//) { # Load line @@ -542,7 +547,7 @@ sub generate_syslinux_config($$$$) 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); + die('Too 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"; @@ -567,24 +572,36 @@ sub generate_grub2_config($$$$;$$) { my ($filename, $title, $base, $modules_ref, $preamble, $prolog) = @_; if ($base && substr($base,-1,1) ne '/') { $base = "$base/"; }; + my $dir = dirname($filename); + make_path($dir, { + chmod => 0755, + }); open(my $fg, '>', $filename) or die "$filename: $!"; print $fg "$preamble\n" if $preamble; $title ||= 'novaboot'; print $fg "menuentry $title {\n"; print $fg "$prolog\n" if $prolog; my $first = 1; + my $boot_method = $variables->{BOOT_METHOD} // "multiboot"; + my $module_load_method = "module"; + if ($boot_method eq "linux") { + $module_load_method = "initrd"; + die('Too many "load" lines for Linux kernel') if (scalar(@$modules_ref) > 2); + } foreach (@$modules_ref) { if ($first) { $first = 0; my ($kbin, $kcmd) = split(' ', $_, 2); $kcmd = '' if !defined $kcmd; - print $fg " multiboot ${base}$kbin $kcmd\n"; + print $fg " $boot_method ${base}$kbin $kcmd\n"; } else { my @args = split; - # GRUB2 doesn't pass filename in multiboot info so we have to duplicate it here - $_ = join(' ', ($args[0], @args)); - s|\brom://|$rom_prefix|g; # We do not need to translate path for GRUB2 - print $fg " module $base$_\n"; + if ($boot_method eq "multiboot") { + # GRUB2 doesn't pass filename in multiboot info so we have to duplicate it here + $_ = join(' ', ($args[0], @args)); + s|\brom://|$rom_prefix|g; # We do not need to translate path for GRUB2 + } + print $fg " $module_load_method $base$_\n"; } } print $fg "}\n"; @@ -1000,7 +1017,7 @@ foreach my $script (@scripts) { my $istty = -t STDOUT && ($ENV{'TERM'} || 'dumb') ne 'dumb'; my $progress = $istty ? "--progress" : ""; if ($files) { - system_verbose("rsync $progress -RLp $rsync_flags $files $real_server"); + system_verbose("rsync $progress -RL --chmod=ugo=rwX $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); @@ -1126,10 +1143,19 @@ if (defined $dhcp_tftp) open(my $fh, '>', 'dhcpd.conf'); 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; - filename \"bin/boot/grub/pxegrub.pxe\"; - next-server 10.23.23.1; + print $fh " +subnet 10.23.23.0 netmask 255.255.255.0 { + range 10.23.23.10 10.23.23.100; + next-server 10.23.23.1; +} +class \"pxe-clients\" { + match option vendor-class-identifier; +} +subclass \"pxe-clients\" \"PXEClient:Arch:00000:UNDI:002001\" { + option bootfile-name \"boot/grub/i386-pc/core.0\"; +} +subclass \"pxe-clients\" \"PXEClient:Arch:00007:UNDI:003016\" { + option bootfile-name \"boot/grub/x86_64-efi/core.efi\"; } host server { hardware ethernet $mac; @@ -1149,16 +1175,28 @@ host server { if (defined $dhcp_tftp || defined $tftp) { $tftp_port ||= 69; + my $tftp_root = "$builddir"; + $tftp_root = "$server" if(defined $server); + + # Prepare a GRUB netboot directory + system_verbose("grub-mknetdir --net-directory=$tftp_root") if (defined $grub2_config); + + # Generate TFTP mapfile + open(my $fh, '>', "$tftp_root/mapfile"); + print $fh "# Some PXE clients (mainly UEFI) have bug. They add zero byte to the end of the +# path name. This rule removes it +r \\.efi.* \\.efi"; + close($fh); # 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"); + system_verbose("sudo in.tftpd --listen --secure -v -v -v --pidfile tftpd.pid -m mapfile --address :$tftp_port $tftp_root"); # Kill server when we die $SIG{__DIE__} = sub { system_verbose('sudo pkill --pidfile=dhcpd.pid') if (defined $dhcp_tftp); - system_verbose('sudo pkill --pidfile=tftpd.pid'); }; + system_verbose("sudo pkill --pidfile=$tftp_root/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); }; + $SIG{INT} = sub { system_verbose("sudo pkill --pidfile=$tftp_root/tftpd.pid"); exit(0); }; } ### AMT IDE-R @@ -1257,8 +1295,9 @@ if (defined $uboot) { die "No '--uboot-addr kernel' given" unless $uboot_addr{kernel}; $exp->send("tftpboot $uboot_addr{kernel} $prefix$kbin\n"); $exp->expect(15, - [qr/##/, sub { exp_continue; }], - $uboot_prompt) || die "Kernel load: " . ($! || "timeout"); + $uboot_prompt, + [qr/#/, sub { exp_continue; }] + ) || die "Kernel load: " . ($! || "timeout"); } if (defined $dtb) { die "No '--uboot-addr fdt' given" unless $uboot_addr{fdt}; @@ -1400,13 +1439,13 @@ to configure novaboot for them. The setups are: This requires to configure everything on the laptop side, including a serial line connection (L, L, ...), power on/off/reset commands (L, ...), TFTP server -(L, L...), device IP addresses, etc. +(L, L...), device IP addresses, etc. =item B: Laptop, target device and external TFTP server Like the previous setup, but the TFTP (and maybe DHCP) configuration is handled by a server. Novaboot users need to understand where to -copy their files to the TFTP server (L) and which IP +copy their files to the TFTP server (L) and which IP addresses their target will get, but do not need to configure the servers themselves. @@ -1450,7 +1489,7 @@ with all other files needed for booting to a remote TFTP server. Then use a TCP/IP-controlled relay/serial-to-TCP converter to reset the target and receive its serial output. - ./mylinux --grub2 --server=192.168.1.1:/tftp --iprelay=192.168.1.2 + ./mylinux --grub2 --copy=192.168.1.1:/tftp --iprelay=192.168.1.2 Alternatively, you can put these switches to the configuration file and run: @@ -1602,7 +1641,7 @@ Configures novaboot to control the target via C running remotely via SSH. Using this option is the same as specifying B<--remote-cmd>, -B<--remote-expect>, B<--server> B<--rsync-flags>, B<--prefix> and +B<--remote-expect>, B<--copy> B<--rsync-flags>, B<--prefix> and B<--reset-cmd> manually in a way compatible with C. The server can be configured to provide other, safe bootloader-related options, to the client. When this happens, novaboot prints them to @@ -1717,10 +1756,14 @@ Alias for B<--prefix>. =item --grub2[=I] Generate GRUB2 menu entry in I. If I is not -specified F is used. The content of the menu entry can be +specified F<./boot/grub/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. +GRUB2 can boot multiboot-compliant kernels and a few kernels with specific +support. L could be used to specify the command used by GRUB2 to +load the kernel. See L. + To use the generated menu entry on your development machine that uses GRUB2, append the following snippet to F file and regenerate your grub configuration, @@ -1910,6 +1953,10 @@ of the novaboot script (see also B<--name>). =item --server[=[[user@]server:]path] +Alias of B<--copy> (kept for backward compatibility). + +=item --copy[=[[user@]server:]path] + Copy all files needed for booting to another location. The files will be copied (by B tool) to the directory I. If the I contains string $NAME, it will be replaced with the name of the @@ -1918,13 +1965,13 @@ novaboot script (see also B<--name>). =item --rsync-flags=I Specifies I to append to F command line when -copying files as a result of I<--server> option. +copying files as a result of I<--copy> option. =item --concat -If B<--server> is used and its value ends with $NAME, then after +If B<--copy> is used and its value ends with $NAME, then after copying the files, a new bootloader configuration file (e.g. menu.lst) -is created at I, i.e. the path specified by B<--server> +is created at I, i.e. the path specified by B<--copy> with $NAME part removed. The content of the file is created by concatenating all files of the same name from all subdirectories of I found on the "server". @@ -2273,6 +2320,11 @@ The following variables are interpreted in the novaboot script: =over 8 +=item BOOT_METHOD + +Specifies the way GRUB2 boots the kernel. For kernels with multiboot +support use C method (the default). For Linux kernel use C method. + =item BUILDDIR Novaboot chdir()s to this directory before file generation phase. The @@ -2378,11 +2430,11 @@ 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: - $targets{'mybox'} = '--server=boot:/tftproot --serial=/dev/ttyUSB0 --grub', + $targets{'mybox'} = '--copy=boot:/tftproot --serial=/dev/ttyUSB0 --grub', then the following two commands are equivalent: - ./myos --server=boot:/tftproot --serial=/dev/ttyUSB0 --grub + ./myos --copy=boot:/tftproot --serial=/dev/ttyUSB0 --grub ./myos -t mybox =back