# Class to handle tagged images
# Placed under GNU Public License by Ken Yap, April 2000
package Nbi;
use strict;
use IO::Seekable;
use constant;
use constant TFTPBLOCKSIZE => 512;
# This is correct for the current version of the netboot specs
# Note: reverse of the way it is in the specs because of Intel byte order
use constant MAGIC => "\x36\x13\x03\x1B";
# This is needed at the end of the boot block, again byte reversed
use constant MAGIC2 => "\x55\xAA";
# This is defined by the bootrom layout
use constant HEADERSIZE => 512;
use vars qw($libdir $bootseg $bootoff @segdescs);
sub new {
my $class = shift;
$libdir = shift;
my $self = {};
bless $self, $class;
# $self->_initialize();
return $self;
}
sub add_header ($$$$$)
{
my ($class, $vendorinfo, $headerseg, $bootseg, $bootoff) = @_;
my ($vilen);
$vilen = length($vendorinfo);
$vilen += 4; # three plus one for null byte
$vilen &= ~0x3; # round to multiple of 4
push(@segdescs, pack("A4V3a$vilen",
MAGIC,
($vilen << 2) + 4,
$headerseg << 16,
($bootseg << 16) + $bootoff,
$vendorinfo));
}
sub add_pm_header ($$$$$)
{
my ($class, $vendorinfo, $headerseg, $bootaddr, $progreturns) = @_;
my ($vilen);
$vilen = length($vendorinfo);
$vilen += 4; # three plus one for null byte
$vilen &= ~0x3; # round to multiple of 4
push(@segdescs, pack("A4V3a$vilen",
MAGIC,
(($vilen << 2) + 4) | (1 << 31) | ($progreturns << 8),
$headerseg << 16,
$bootaddr,
$vendorinfo));
}
sub roundup ($$)
{
# Round up to next multiple of $blocksize, assumes that it's a power of 2
my ($size, $blocksize) = @_;
# Default to TFTPBLOCKSIZE if not specified
$blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
return ($size + $blocksize - 1) & ~($blocksize - 1);
}
# Grab N bytes from a file
sub peek_file ($$$$)
{
my ($class, $descriptor, $dataptr, $datalen) = @_;
my ($file, $fromoff, $status);
$file = $$descriptor{'file'} if exists $$descriptor{'file'};
$fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
return 0 if !defined($file) or !open(R, "$file");
binmode(R);
if (defined($fromoff)) {
return 0 if !seek(R, $fromoff, SEEK_SET);
}
# Read up to $datalen bytes
$status = read(R, $$dataptr, $datalen);
close(R);
return ($status);
}
# Add a segment descriptor from a file or a string
sub add_segment ($$$)
{
my ($class, $descriptor, $vendorinfo) = @_;
my ($file, $string, $segment, $len, $maxlen, $fromoff, $align,
$id, $end, $vilen);
$end = 0;
$file = $$descriptor{'file'} if exists $$descriptor{'file'};
$string = $$descriptor{'string'} if exists $$descriptor{'string'};
$segment = $$descriptor{'segment'} if exists $$descriptor{'segment'};
$len = $$descriptor{'len'} if exists $$descriptor{'len'};
$maxlen = $$descriptor{'maxlen'} if exists $$descriptor{'maxlen'};
$fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
$align = $$descriptor{'align'} if exists $$descriptor{'align'};
$id = $$descriptor{'id'} if exists $$descriptor{'id'};
$end = $$descriptor{'end'} if exists $$descriptor{'end'};
if (!defined($len)) {
if (defined($string)) {
$len = length($string);
} else {
if (defined($fromoff)) {
$len = (-s $file) - $fromoff;
} else {
$len = -s $file;
}
return 0 if !defined($len); # no such file
}
}
if (defined($align)) {
$len = &roundup($len, $align);
} else {
$len = &roundup($len);
}
$maxlen = $len if (!defined($maxlen));
if (!defined($vendorinfo)) {
push(@segdescs, pack('V4',
4 + ($id << 8) + ($end << 26),
$segment << 4,
$len,
$maxlen));
} else {
$vilen = length($vendorinfo);
$vilen += 3; # three plus one for null byte
$vilen &= ~0x3; # round to multiple of 4
push(@segdescs, pack("V4a$vilen",
($vilen << 2) + 4 + ($id << 8) + ($end << 26),
$segment << 4,
$len,
$maxlen,
$vendorinfo));
}
return ($len); # assumes always > 0
}
sub pad_with_nulls ($$)
{
my ($i, $blocksize) = @_;
$blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
# Pad with nulls to next block boundary
$i %= $blocksize;
print "\0" x ($blocksize - $i) if ($i != 0);
}
# Copy data from file to stdout
sub copy_file ($$)
{
my ($class, $descriptor) = @_;
my ($i, $file, $fromoff, $align, $len, $seglen, $nread, $data, $status);
$file = $$descriptor{'file'} if exists $$descriptor{'file'};
$fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
$align = $$descriptor{'align'} if exists $$descriptor{'align'};
$len = $$descriptor{'len'} if exists $$descriptor{'len'};
return 0 if !open(R, "$file");
if (defined($fromoff)) {
return 0 if !seek(R, $fromoff, SEEK_SET);
$len = (-s $file) - $fromoff if !defined($len);
} else {
$len = -s $file if !defined($len);
}
binmode(R);
# Copy file in TFTPBLOCKSIZE chunks
$nread = 0;
while ($nread != $len) {
$status = read(R, $data, TFTPBLOCKSIZE);
last if (!defined($status) or $status == 0);
print $data;
$nread += $status;
}
close(R);
if (defined($align)) {
&pad_with_nulls($nread, $align);
} else {
&pad_with_nulls($nread);
}
return ($nread);
}
# Copy data from string to stdout
sub copy_string ($$)
{
my ($class, $descriptor) = @_;
my ($i, $string, $len, $align);
$string = $$descriptor{'string'} if exists $$descriptor{'string'};
$len = $$descriptor{'len'} if exists $$descriptor{'len'};
$align = $$descriptor{'align'} if exists $$descriptor{'align'};
return 0 if !defined($string);
$len = length($string) if !defined($len);
print substr($string, 0, $len);
defined($align) ? &pad_with_nulls($len, $align) : &pad_with_nulls($len);
return ($len);
}
sub dump_segments {
my ($s, $len);
$len = 0;
while ($s = shift(@segdescs)) {
$len += length($s);
print $s;
}
print "\0" x (HEADERSIZE - 2 - $len), MAGIC2;
}
# This empty for now, but is available as a hook to do any actions
# before closing the image file
sub finalise_image {
}
@segdescs = ();
1;
syntax highlighted by Code2HTML, v. 0.9.1