Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / Nbi.pm
1 # Class to handle tagged images
2 # Placed under GNU Public License by Ken Yap, April 2000
3
4 package Nbi;
5
6 use strict;
7 use IO::Seekable;
8
9 use constant;
10 use constant TFTPBLOCKSIZE => 512;
11 # This is correct for the current version of the netboot specs
12 # Note: reverse of the way it is in the specs because of Intel byte order
13 use constant MAGIC => "\x36\x13\x03\x1B";
14 # This is needed at the end of the boot block, again byte reversed
15 use constant MAGIC2 => "\x55\xAA";
16 # This is defined by the bootrom layout
17 use constant HEADERSIZE => 512;
18
19 use vars qw($libdir $bootseg $bootoff @segdescs);
20
21 sub new {
22         my $class = shift;
23         $libdir = shift;
24         my $self = {};
25         bless $self, $class;
26 #       $self->_initialize();
27         return $self;
28 }
29
30 sub add_header ($$$$$)
31 {
32         my ($class, $vendorinfo, $headerseg, $bootseg, $bootoff) = @_;
33         my ($vilen);
34
35         $vilen = length($vendorinfo);
36         $vilen += 4;            # three plus one for null byte
37         $vilen &= ~0x3; # round to multiple of 4
38         push(@segdescs, pack("A4V3a$vilen",
39                 MAGIC,
40                 ($vilen << 2) + 4,
41                 $headerseg << 16,
42                 ($bootseg << 16) + $bootoff,
43                 $vendorinfo));
44 }
45
46 sub add_pm_header ($$$$$)
47 {
48         my ($class, $vendorinfo, $headerseg, $bootaddr, $progreturns) = @_;
49         my ($vilen);
50
51         $vilen = length($vendorinfo);
52         $vilen += 4;            # three plus one for null byte
53         $vilen &= ~0x3; # round to multiple of 4
54         push(@segdescs, pack("A4V3a$vilen",
55                 MAGIC,
56                 (($vilen << 2) + 4) | (1 << 31) | ($progreturns << 8),
57                 $headerseg << 16,
58                 $bootaddr,
59                 $vendorinfo));
60 }
61
62 sub roundup ($$)
63 {
64 # Round up to next multiple of $blocksize, assumes that it's a power of 2
65         my ($size, $blocksize) = @_;
66
67         # Default to TFTPBLOCKSIZE if not specified
68         $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
69         return ($size + $blocksize - 1) & ~($blocksize - 1);
70 }
71
72 # Grab N bytes from a file
73 sub peek_file ($$$$)
74 {
75         my ($class, $descriptor, $dataptr, $datalen) = @_;
76         my ($file, $fromoff, $status);
77
78         $file = $$descriptor{'file'} if exists $$descriptor{'file'};
79         $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
80         return 0 if !defined($file) or !open(R, "$file");
81         binmode(R);
82         if (defined($fromoff)) {
83                 return 0 if !seek(R, $fromoff, SEEK_SET);
84         }
85         # Read up to $datalen bytes
86         $status = read(R, $$dataptr, $datalen);
87         close(R);
88         return ($status);
89 }
90
91 # Add a segment descriptor from a file or a string
92 sub add_segment ($$$)
93 {
94         my ($class, $descriptor, $vendorinfo) = @_;
95         my ($file, $string, $segment, $len, $maxlen, $fromoff, $align,
96                 $id, $end, $vilen);
97
98         $end = 0;
99         $file = $$descriptor{'file'} if exists $$descriptor{'file'};
100         $string = $$descriptor{'string'} if exists $$descriptor{'string'};
101         $segment = $$descriptor{'segment'} if exists $$descriptor{'segment'};
102         $len = $$descriptor{'len'} if exists $$descriptor{'len'};
103         $maxlen = $$descriptor{'maxlen'} if exists $$descriptor{'maxlen'};
104         $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
105         $align = $$descriptor{'align'} if exists $$descriptor{'align'};
106         $id = $$descriptor{'id'} if exists $$descriptor{'id'};
107         $end = $$descriptor{'end'} if exists $$descriptor{'end'};
108         if (!defined($len)) {
109                 if (defined($string)) {
110                         $len = length($string);
111                 } else {
112                         if (defined($fromoff)) {
113                                 $len = (-s $file) - $fromoff;
114                         } else {
115                                 $len = -s $file;
116                         }
117                         return 0 if !defined($len);             # no such file
118                 }
119         }
120         if (defined($align)) {
121                 $len = &roundup($len, $align);
122         } else {
123                 $len = &roundup($len);
124         }
125         $maxlen = $len if (!defined($maxlen));
126         if (!defined($vendorinfo)) {
127                 push(@segdescs, pack('V4',
128                         4 + ($id << 8) + ($end << 26),
129                         $segment << 4,
130                         $len,
131                         $maxlen));
132         } else {
133                 $vilen = length($vendorinfo);
134                 $vilen += 3;           # three plus one for null byte
135                 $vilen &= ~0x3;        # round to multiple of 4
136                 push(@segdescs, pack("V4a$vilen",
137                         ($vilen << 2) + 4 + ($id << 8) + ($end << 26),
138                         $segment << 4,
139                         $len,
140                         $maxlen,
141                         $vendorinfo));
142         }
143         return ($len);                  # assumes always > 0
144 }
145
146 sub pad_with_nulls ($$)
147 {
148         my ($i, $blocksize) = @_;
149
150         $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
151         # Pad with nulls to next block boundary
152         $i %= $blocksize;
153         print "\0" x ($blocksize - $i) if ($i != 0);
154 }
155
156 # Copy data from file to stdout
157 sub copy_file ($$)
158 {
159         my ($class, $descriptor) = @_;
160         my ($i, $file, $fromoff, $align, $len, $seglen, $nread, $data, $status);
161
162         $file = $$descriptor{'file'} if exists $$descriptor{'file'};
163         $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
164         $align = $$descriptor{'align'} if exists $$descriptor{'align'};
165         $len = $$descriptor{'len'} if exists $$descriptor{'len'};
166         return 0 if !open(R, "$file");
167         if (defined($fromoff)) {
168                 return 0 if !seek(R, $fromoff, SEEK_SET);
169                 $len = (-s $file) - $fromoff if !defined($len);
170         } else {
171                 $len = -s $file if !defined($len);
172         }
173         binmode(R);
174         # Copy file in TFTPBLOCKSIZE chunks
175         $nread = 0;
176         while ($nread != $len) {
177                 $status = read(R, $data, TFTPBLOCKSIZE);
178                 last if (!defined($status) or $status == 0);
179                 print $data;
180                 $nread += $status;
181         }
182         close(R);
183         if (defined($align)) {
184                 &pad_with_nulls($nread, $align);
185         } else {
186                 &pad_with_nulls($nread);
187         }
188         return ($nread);
189 }
190
191 # Copy data from string to stdout
192 sub copy_string ($$)
193 {
194         my ($class, $descriptor) = @_;
195         my ($i, $string, $len, $align);
196
197         $string = $$descriptor{'string'} if exists $$descriptor{'string'};
198         $len = $$descriptor{'len'} if exists $$descriptor{'len'};
199         $align = $$descriptor{'align'} if exists $$descriptor{'align'};
200         return 0 if !defined($string);
201         $len = length($string) if !defined($len);
202         print substr($string, 0, $len);
203         defined($align) ? &pad_with_nulls($len, $align) : &pad_with_nulls($len);
204         return ($len);
205 }
206
207 sub dump_segments {
208         my ($s, $len);
209
210         $len = 0;
211         while ($s = shift(@segdescs)) {
212                 $len += length($s);
213                 print $s;
214         }
215         print "\0" x (HEADERSIZE - 2 - $len), MAGIC2;
216 }
217
218 # This empty for now, but is available as a hook to do any actions
219 # before closing the image file
220
221 sub finalise_image {
222 }
223
224 @segdescs = ();
225
226 1;