#include <errno.h>
#include <assert.h>
#include <realmode.h>
#include <gateA20.h>
#include <memsizes.h>
#include <basemem_packet.h>
#include <gpxe/uaccess.h>
#include <gpxe/segment.h>
#include <gpxe/init.h>
#include <gpxe/netdevice.h>
#include <gpxe/fakedhcp.h>
#include <gpxe/image.h>
#include <gpxe/features.h>
Go to the source code of this file.
Data Structures | |
| struct | imgheader |
| An NBI image header. More... | |
| struct | segheader |
| An NBI segment header. More... | |
| struct | ebinfo |
Defines | |
| #define | NBI_MAGIC 0x1B031336UL |
| NBI magic number. | |
| #define | NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 ) |
| #define | NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 ) |
| #define | NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) ) |
| #define | NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) ) |
| #define | NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) ) |
| #define | NBI_HEADER_LENGTH 512 |
| NBI header length. | |
| #define | NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 ) |
| #define | NBI_LOADADDR_ABS 0x00 |
| #define | NBI_LOADADDR_AFTER 0x01 |
| #define | NBI_LOADADDR_END 0x02 |
| #define | NBI_LOADADDR_BEFORE 0x03 |
| #define | NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) ) |
Functions | |
| FEATURE (FEATURE_IMAGE,"NBI", DHCP_EB_FEATURE_NBI, 1) | |
| struct image_type nbi_image_type | __image_type (PROBE_NORMAL) |
| NBI image type. | |
| static int | nbi_prepare_segment (struct image *image, size_t offset __unused, userptr_t dest, size_t filesz, size_t memsz) |
| Prepare a segment for an NBI image. | |
| static int | nbi_load_segment (struct image *image, size_t offset, userptr_t dest, size_t filesz, size_t memsz __unused) |
| Load a segment for an NBI image. | |
| static int | nbi_process_segments (struct image *image, struct imgheader *imgheader, int(*process)(struct image *image, size_t offset, userptr_t dest, size_t filesz, size_t memsz)) |
| Process segments of an NBI image. | |
| static int | nbi_load (struct image *image) |
| Load an NBI image into memory. | |
| static int | nbi_boot16 (struct image *image, struct imgheader *imgheader) |
| Boot a 16-bit NBI image. | |
| static int | nbi_boot32 (struct image *image, struct imgheader *imgheader) |
| Boot a 32-bit NBI image. | |
| static int | nbi_prepare_dhcp (struct image *image) |
| Prepare DHCP parameter block for NBI image. | |
| static int | nbi_exec (struct image *image) |
| Execute a loaded NBI image. | |
Variables | |
| static struct ebinfo | loaderinfo |
| Info passed to NBI image. | |
The Net Boot Image format is defined by the "Draft Net Boot Image Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now considered to be a legacy format, but it still included because a large amount of software (e.g. nymph, LTSP) makes use of NBI files.
Etherboot does not implement the INT 78 callback interface described by the NBI specification. For a callback interface on x86 architecture, use PXE.
Definition in file nbi.c.
| #define NBI_MAGIC 0x1B031336UL |
| #define NBI_LENGTH | ( | len | ) | ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) ) |
| #define NBI_PROGRAM_RETURNS | ( | flags | ) | ( (flags) & ( 1 << 8 ) ) |
| #define NBI_LINEAR_EXEC_ADDR | ( | flags | ) | ( (flags) & ( 1 << 31 ) ) |
| #define NBI_HEADER_LENGTH 512 |
NBI header length.
Definition at line 67 of file nbi.c.
Referenced by nbi_load(), and nbi_process_segments().
| #define NBI_LOADADDR_FLAGS | ( | flags | ) | ( (flags) & 0x03 ) |
| #define NBI_LOADADDR_ABS 0x00 |
| #define NBI_LOADADDR_AFTER 0x01 |
| #define NBI_LOADADDR_END 0x02 |
| #define NBI_LOADADDR_BEFORE 0x03 |
| #define NBI_LAST_SEGHEADER | ( | flags | ) | ( (flags) & ( 1 << 2 ) ) |
| FEATURE | ( | FEATURE_IMAGE | , | |
| "NBI" | , | |||
| DHCP_EB_FEATURE_NBI | , | |||
| 1 | ||||
| ) |
| struct image_type nbi_image_type __image_type | ( | PROBE_NORMAL | ) | [read] |
NBI image type.
| static int nbi_prepare_segment | ( | struct image * | image, | |
| size_t offset | __unused, | |||
| userptr_t | dest, | |||
| size_t | filesz, | |||
| size_t | memsz | |||
| ) | [static] |
Prepare a segment for an NBI image.
| image | NBI image | |
| offset | Offset within NBI image | |
| filesz | Length of initialised-data portion of the segment | |
| memsz | Total length of the segment | |
| src | Source for initialised data |
| rc | Return status code |
Definition at line 116 of file nbi.c.
References DBGC, prep_segment(), and strerror().
Referenced by nbi_load().
00117 { 00118 int rc; 00119 00120 if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) { 00121 DBGC ( image, "NBI %p could not prepare segment: %s\n", 00122 image, strerror ( rc ) ); 00123 return rc; 00124 } 00125 00126 return 0; 00127 }
| static int nbi_load_segment | ( | struct image * | image, | |
| size_t | offset, | |||
| userptr_t | dest, | |||
| size_t | filesz, | |||
| size_t memsz | __unused | |||
| ) | [static] |
Load a segment for an NBI image.
| image | NBI image | |
| offset | Offset within NBI image | |
| filesz | Length of initialised-data portion of the segment | |
| memsz | Total length of the segment | |
| src | Source for initialised data |
| rc | Return status code |
Definition at line 139 of file nbi.c.
References image::data, and memcpy_user().
Referenced by nbi_load().
00141 { 00142 memcpy_user ( dest, 0, image->data, offset, filesz ); 00143 return 0; 00144 }
| static int nbi_process_segments | ( | struct image * | image, | |
| struct imgheader * | imgheader, | |||
| int(*)(struct image *image, size_t offset, userptr_t dest, size_t filesz, size_t memsz) | process | |||
| ) | [static] |
Process segments of an NBI image.
| image | NBI image | |
| imgheader | Image header information | |
| process | Function to call for each segment |
| rc | Return status code |
Definition at line 154 of file nbi.c.
References assert, copy_from_user(), image::data, DBGC, ENOEXEC, extmemsize(), segheader::flags, segheader::imglength, image::len, segheader::length, imgheader::length, segheader::loadaddr, imgheader::location, segheader::memlength, NBI_HEADER_LENGTH, NBI_LAST_SEGHEADER, NBI_LENGTH, NBI_LOADADDR_ABS, NBI_LOADADDR_AFTER, NBI_LOADADDR_BEFORE, NBI_LOADADDR_END, NBI_LOADADDR_FLAGS, segoff::offset, phys_to_user(), real_to_user(), segoff::segment, and userptr_add().
Referenced by nbi_load().
00160 { 00161 struct segheader sh; 00162 size_t offset = 0; 00163 size_t sh_off; 00164 userptr_t dest; 00165 size_t filesz; 00166 size_t memsz; 00167 int rc; 00168 00169 /* Copy image header to target location */ 00170 dest = real_to_user ( imgheader->location.segment, 00171 imgheader->location.offset ); 00172 filesz = memsz = NBI_HEADER_LENGTH; 00173 if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 ) 00174 return rc; 00175 offset += filesz; 00176 00177 /* Process segments in turn */ 00178 sh_off = NBI_LENGTH ( imgheader->length ); 00179 do { 00180 /* Read segment header */ 00181 copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) ); 00182 if ( sh.length == 0 ) { 00183 /* Avoid infinite loop? */ 00184 DBGC ( image, "NBI %p invalid segheader length 0\n", 00185 image ); 00186 return -ENOEXEC; 00187 } 00188 00189 /* Calculate segment load address */ 00190 switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) { 00191 case NBI_LOADADDR_ABS: 00192 dest = phys_to_user ( sh.loadaddr ); 00193 break; 00194 case NBI_LOADADDR_AFTER: 00195 dest = userptr_add ( dest, memsz + sh.loadaddr ); 00196 break; 00197 case NBI_LOADADDR_BEFORE: 00198 dest = userptr_add ( dest, -sh.loadaddr ); 00199 break; 00200 case NBI_LOADADDR_END: 00201 /* Not correct according to the spec, but 00202 * maintains backwards compatibility with 00203 * previous versions of Etherboot. 00204 */ 00205 dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024 00206 - sh.loadaddr ); 00207 break; 00208 default: 00209 /* Cannot be reached */ 00210 assert ( 0 ); 00211 } 00212 00213 /* Process this segment */ 00214 filesz = sh.imglength; 00215 memsz = sh.memlength; 00216 if ( ( offset + filesz ) > image->len ) { 00217 DBGC ( image, "NBI %p segment outside file\n", image ); 00218 return -ENOEXEC; 00219 } 00220 if ( ( rc = process ( image, offset, dest, 00221 filesz, memsz ) ) != 0 ) { 00222 return rc; 00223 } 00224 offset += filesz; 00225 00226 /* Next segheader */ 00227 sh_off += NBI_LENGTH ( sh.length ); 00228 if ( sh_off >= NBI_HEADER_LENGTH ) { 00229 DBGC ( image, "NBI %p header overflow\n", image ); 00230 return -ENOEXEC; 00231 } 00232 00233 } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) ); 00234 00235 if ( offset != image->len ) { 00236 DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n", 00237 image, image->len, offset ); 00238 return -ENOEXEC; 00239 } 00240 00241 return 0; 00242 }
| static int nbi_load | ( | struct image * | image | ) | [static] |
Load an NBI image into memory.
| rc | Return status code |
Definition at line 250 of file nbi.c.
References copy_from_user(), image::data, DBGC, ENOEXEC, image::len, imgheader::location, imgheader::magic, NBI_HEADER_LENGTH, nbi_load_segment(), NBI_MAGIC, nbi_prepare_segment(), nbi_process_segments(), segoff::offset, image::priv, real_to_user(), segoff::segment, image::type, and image::user.
00250 { 00251 struct imgheader imgheader; 00252 int rc; 00253 00254 /* If we don't have enough data give up */ 00255 if ( image->len < NBI_HEADER_LENGTH ) { 00256 DBGC ( image, "NBI %p too short for an NBI image\n", image ); 00257 return -ENOEXEC; 00258 } 00259 00260 /* Check image header */ 00261 copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) ); 00262 if ( imgheader.magic != NBI_MAGIC ) { 00263 DBGC ( image, "NBI %p has no NBI signature\n", image ); 00264 return -ENOEXEC; 00265 } 00266 00267 /* This is an NBI image, valid or otherwise */ 00268 if ( ! image->type ) 00269 image->type = &nbi_image_type; 00270 00271 DBGC ( image, "NBI %p placing header at %hx:%hx\n", image, 00272 imgheader.location.segment, imgheader.location.offset ); 00273 00274 /* NBI files can have overlaps between segments; the bss of 00275 * one segment may overlap the initialised data of another. I 00276 * assume this is a design flaw, but there are images out 00277 * there that we need to work with. We therefore do two 00278 * passes: first to initialise the segments, then to copy the 00279 * data. This avoids zeroing out already-copied data. 00280 */ 00281 if ( ( rc = nbi_process_segments ( image, &imgheader, 00282 nbi_prepare_segment ) ) != 0 ) 00283 return rc; 00284 if ( ( rc = nbi_process_segments ( image, &imgheader, 00285 nbi_load_segment ) ) != 0 ) 00286 return rc; 00287 00288 /* Record header address in image private data field */ 00289 image->priv.user = real_to_user ( imgheader.location.segment, 00290 imgheader.location.offset ); 00291 00292 return 0; 00293 }
Boot a 16-bit NBI image.
| imgheader | Image header information |
| rc | Return status code, if image returns |
Definition at line 301 of file nbi.c.
References __asm__(), __from_data16, basemem_packet, DBGC, imgheader::execaddr, gateA20_set(), gateA20_unset(), imgheader::location, segoff::offset, REAL_CODE, segoff::segment, and imgheader::segoff.
Referenced by nbi_exec().
00301 { 00302 int discard_D, discard_S, discard_b; 00303 int rc; 00304 00305 DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, 00306 imgheader->execaddr.segoff.segment, 00307 imgheader->execaddr.segoff.offset ); 00308 00309 gateA20_unset(); 00310 00311 __asm__ __volatile__ ( 00312 REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */ 00313 "pushw %%bx\n\t" 00314 "pushl %%esi\n\t" /* location */ 00315 "pushw %%cs\n\t" /* lcall execaddr */ 00316 "call 1f\n\t" 00317 "jmp 2f\n\t" 00318 "\n1:\n\t" 00319 "pushl %%edi\n\t" 00320 "lret\n\t" 00321 "\n2:\n\t" 00322 "addw $8,%%sp\n\t" /* clean up stack */ ) 00323 : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), 00324 "=b" ( discard_b ) 00325 : "D" ( imgheader->execaddr.segoff ), 00326 "S" ( imgheader->location ), 00327 "b" ( __from_data16 ( basemem_packet ) ) 00328 : "ecx", "edx", "ebp" ); 00329 00330 gateA20_set(); 00331 00332 return rc; 00333 }
Boot a 32-bit NBI image.
| imgheader | Image header information |
| rc | Return status code, if image returns |
Definition at line 341 of file nbi.c.
References __asm__(), basemem_packet, DBGC, imgheader::execaddr, imgheader::linear, imgheader::location, segoff::offset, PHYS_CODE, segoff::segment, and virt_to_phys().
Referenced by nbi_exec().
00341 { 00342 int discard_D, discard_S, discard_b; 00343 int rc; 00344 00345 DBGC ( image, "NBI %p executing 32-bit image at %lx\n", 00346 image, imgheader->execaddr.linear ); 00347 00348 /* no gateA20_unset for PM call */ 00349 00350 /* Jump to OS with flat physical addressing */ 00351 __asm__ __volatile__ ( 00352 PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */ 00353 "pushl %%esi\n\t" /* imgheader */ 00354 "pushl %%eax\n\t" /* loaderinfo */ 00355 "call *%%edi\n\t" 00356 "addl $12, %%esp\n\t" /* clean up stack */ ) 00357 : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), 00358 "=b" ( discard_b ) 00359 : "D" ( imgheader->execaddr.linear ), 00360 "S" ( ( imgheader->location.segment << 4 ) + 00361 imgheader->location.offset ), 00362 "b" ( virt_to_phys ( basemem_packet ) ), 00363 "a" ( virt_to_phys ( &loaderinfo ) ) 00364 : "ecx", "edx", "ebp", "memory" ); 00365 00366 return rc; 00367 }
| static int nbi_prepare_dhcp | ( | struct image * | image | ) | [static] |
Prepare DHCP parameter block for NBI image.
| rc | Return status code |
Definition at line 375 of file nbi.c.
References basemem_packet, create_fakedhcpack(), DBGC, ENODEV, and last_opened_netdev().
Referenced by nbi_exec().
00375 { 00376 struct net_device *boot_netdev; 00377 int rc; 00378 00379 boot_netdev = last_opened_netdev(); 00380 if ( ! boot_netdev ) { 00381 DBGC ( image, "NBI %p could not identify a network device\n", 00382 image ); 00383 return -ENODEV; 00384 } 00385 00386 if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet, 00387 sizeof ( basemem_packet ) ) ) != 0 ) { 00388 DBGC ( image, "NBI %p failed to build DHCP packet\n", image ); 00389 return rc; 00390 } 00391 00392 return 0; 00393 }
| static int nbi_exec | ( | struct image * | image | ) | [static] |
Execute a loaded NBI image.
| rc | Return status code |
Definition at line 401 of file nbi.c.
References copy_from_user(), DBGC, imgheader::flags, nbi_boot16(), nbi_boot32(), NBI_LINEAR_EXEC_ADDR, nbi_prepare_dhcp(), NBI_PROGRAM_RETURNS, image::priv, shutdown(), SHUTDOWN_BOOT, and image::user.
00401 { 00402 struct imgheader imgheader; 00403 int may_return; 00404 int rc; 00405 00406 copy_from_user ( &imgheader, image->priv.user, 0, 00407 sizeof ( imgheader ) ); 00408 00409 /* Prepare DHCP option block */ 00410 if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 ) 00411 return rc; 00412 00413 /* Shut down now if NBI image will not return */ 00414 may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); 00415 if ( ! may_return ) 00416 shutdown ( SHUTDOWN_BOOT ); 00417 00418 /* Execute NBI image */ 00419 if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { 00420 rc = nbi_boot32 ( image, &imgheader ); 00421 } else { 00422 rc = nbi_boot16 ( image, &imgheader ); 00423 } 00424 00425 if ( ! may_return ) { 00426 /* Cannot continue after shutdown() called */ 00427 DBGC ( image, "NBI %p returned %d from non-returnable image\n", 00428 image, rc ); 00429 while ( 1 ) {} 00430 } 00431 00432 DBGC ( image, "NBI %p returned %d\n", image, rc ); 00433 00434 return rc; 00435 }
struct ebinfo loaderinfo [static] |
1.5.7.1