nbi.c

Go to the documentation of this file.
00001 #include <errno.h>
00002 #include <assert.h>
00003 #include <realmode.h>
00004 #include <gateA20.h>
00005 #include <memsizes.h>
00006 #include <basemem_packet.h>
00007 #include <gpxe/uaccess.h>
00008 #include <gpxe/segment.h>
00009 #include <gpxe/init.h>
00010 #include <gpxe/netdevice.h>
00011 #include <gpxe/fakedhcp.h>
00012 #include <gpxe/image.h>
00013 #include <gpxe/features.h>
00014 
00015 /** @file
00016  *
00017  * NBI image format.
00018  *
00019  * The Net Boot Image format is defined by the "Draft Net Boot Image
00020  * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap.  It is now
00021  * considered to be a legacy format, but it still included because a
00022  * large amount of software (e.g. nymph, LTSP) makes use of NBI files.
00023  *
00024  * Etherboot does not implement the INT 78 callback interface
00025  * described by the NBI specification.  For a callback interface on
00026  * x86 architecture, use PXE.
00027  *
00028  */
00029 
00030 FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 );
00031 
00032 struct image_type nbi_image_type __image_type ( PROBE_NORMAL );
00033 
00034 /**
00035  * An NBI image header
00036  *
00037  * Note that the length field uses a peculiar encoding; use the
00038  * NBI_LENGTH() macro to decode the actual header length.
00039  *
00040  */
00041 struct imgheader {
00042         unsigned long magic;            /**< Magic number (NBI_MAGIC) */
00043         union {
00044                 unsigned char length;   /**< Nibble-coded header length */
00045                 unsigned long flags;    /**< Image flags */
00046         };
00047         segoff_t location;              /**< 16-bit seg:off header location */
00048         union {
00049                 segoff_t segoff;        /**< 16-bit seg:off entry point */
00050                 unsigned long linear;   /**< 32-bit entry point */
00051         } execaddr;
00052 } __attribute__ (( packed ));
00053 
00054 /** NBI magic number */
00055 #define NBI_MAGIC 0x1B031336UL
00056 
00057 /* Interpretation of the "length" fields */
00058 #define NBI_NONVENDOR_LENGTH(len)       ( ( (len) & 0x0f ) << 2 )
00059 #define NBI_VENDOR_LENGTH(len)          ( ( (len) & 0xf0 ) >> 2 )
00060 #define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) )
00061 
00062 /* Interpretation of the "flags" fields */
00063 #define NBI_PROGRAM_RETURNS(flags)      ( (flags) & ( 1 << 8 ) )
00064 #define NBI_LINEAR_EXEC_ADDR(flags)     ( (flags) & ( 1 << 31 ) )
00065 
00066 /** NBI header length */
00067 #define NBI_HEADER_LENGTH       512
00068 
00069 /**
00070  * An NBI segment header
00071  *
00072  * Note that the length field uses a peculiar encoding; use the
00073  * NBI_LENGTH() macro to decode the actual header length.
00074  *
00075  */
00076 struct segheader {
00077         unsigned char length;           /**< Nibble-coded header length */
00078         unsigned char vendortag;        /**< Vendor-defined private tag */
00079         unsigned char reserved;
00080         unsigned char flags;            /**< Segment flags */
00081         unsigned long loadaddr;         /**< Load address */
00082         unsigned long imglength;        /**< Segment length in NBI file */
00083         unsigned long memlength;        /**< Segment length in memory */
00084 };
00085 
00086 /* Interpretation of the "flags" fields */
00087 #define NBI_LOADADDR_FLAGS(flags)       ( (flags) & 0x03 )
00088 #define NBI_LOADADDR_ABS                0x00
00089 #define NBI_LOADADDR_AFTER              0x01
00090 #define NBI_LOADADDR_END                0x02
00091 #define NBI_LOADADDR_BEFORE             0x03
00092 #define NBI_LAST_SEGHEADER(flags)       ( (flags) & ( 1 << 2 ) )
00093 
00094 /* Define a type for passing info to a loaded program */
00095 struct ebinfo {
00096         uint8_t  major, minor;  /* Version */
00097         uint16_t flags;         /* Bit flags */
00098 };
00099 
00100 /** Info passed to NBI image */
00101 static struct ebinfo loaderinfo = {
00102         VERSION_MAJOR, VERSION_MINOR,
00103         0
00104 };
00105 
00106 /**
00107  * Prepare a segment for an NBI image
00108  *
00109  * @v image             NBI image
00110  * @v offset            Offset within NBI image
00111  * @v filesz            Length of initialised-data portion of the segment
00112  * @v memsz             Total length of the segment
00113  * @v src               Source for initialised data
00114  * @ret rc              Return status code
00115  */
00116 static int nbi_prepare_segment ( struct image *image, size_t offset __unused,
00117                                  userptr_t dest, size_t filesz, size_t memsz ){
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 }
00128 
00129 /**
00130  * Load a segment for an NBI image
00131  *
00132  * @v image             NBI image
00133  * @v offset            Offset within NBI image
00134  * @v filesz            Length of initialised-data portion of the segment
00135  * @v memsz             Total length of the segment
00136  * @v src               Source for initialised data
00137  * @ret rc              Return status code
00138  */
00139 static int nbi_load_segment ( struct image *image, size_t offset,
00140                               userptr_t dest, size_t filesz,
00141                               size_t memsz __unused ) {
00142         memcpy_user ( dest, 0, image->data, offset, filesz );
00143         return 0;
00144 }
00145 
00146 /**
00147  * Process segments of an NBI image
00148  *
00149  * @v image             NBI image
00150  * @v imgheader         Image header information
00151  * @v process           Function to call for each segment
00152  * @ret rc              Return status code
00153  */
00154 static int nbi_process_segments ( struct image *image,
00155                                   struct imgheader *imgheader,
00156                                   int ( * process ) ( struct image *image,
00157                                                       size_t offset,
00158                                                       userptr_t dest,
00159                                                       size_t filesz,
00160                                                       size_t memsz ) ) {
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 }
00243 
00244 /**
00245  * Load an NBI image into memory
00246  *
00247  * @v image             NBI image
00248  * @ret rc              Return status code
00249  */
00250 static int nbi_load ( struct image *image ) {
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 }
00294 
00295 /**
00296  * Boot a 16-bit NBI image
00297  *
00298  * @v imgheader         Image header information
00299  * @ret rc              Return status code, if image returns
00300  */
00301 static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
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 }
00334 
00335 /**
00336  * Boot a 32-bit NBI image
00337  *
00338  * @v imgheader         Image header information
00339  * @ret rc              Return status code, if image returns
00340  */
00341 static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
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 }
00368 
00369 /**
00370  * Prepare DHCP parameter block for NBI image
00371  *
00372  * @v image             NBI image
00373  * @ret rc              Return status code
00374  */
00375 static int nbi_prepare_dhcp ( struct image *image ) {
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 }
00394 
00395 /**
00396  * Execute a loaded NBI image
00397  *
00398  * @v image             NBI image
00399  * @ret rc              Return status code
00400  */
00401 static int nbi_exec ( struct image *image ) {
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 }
00436 
00437 /** NBI image type */
00438 struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = {
00439         .name = "NBI",
00440         .load = nbi_load,
00441         .exec = nbi_exec,
00442 };

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1