1#include <errno.h> 2#include <assert.h> 3#include <realmode.h> 4#include <gateA20.h> 5#include <memsizes.h> 6#include <basemem_packet.h> 7#include <gpxe/uaccess.h> 8#include <gpxe/segment.h> 9#include <gpxe/init.h> 10#include <gpxe/netdevice.h> 11#include <gpxe/fakedhcp.h> 12#include <gpxe/image.h> 13#include <gpxe/features.h> 14 15/** @file 16 * 17 * NBI image format. 18 * 19 * The Net Boot Image format is defined by the "Draft Net Boot Image 20 * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now 21 * considered to be a legacy format, but it still included because a 22 * large amount of software (e.g. nymph, LTSP) makes use of NBI files. 23 * 24 * Etherboot does not implement the INT 78 callback interface 25 * described by the NBI specification. For a callback interface on 26 * x86 architecture, use PXE. 27 * 28 */ 29 30FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 ); 31 32struct image_type nbi_image_type __image_type ( PROBE_NORMAL ); 33 34/** 35 * An NBI image header 36 * 37 * Note that the length field uses a peculiar encoding; use the 38 * NBI_LENGTH() macro to decode the actual header length. 39 * 40 */ 41struct imgheader { 42 unsigned long magic; /**< Magic number (NBI_MAGIC) */ 43 union { 44 unsigned char length; /**< Nibble-coded header length */ 45 unsigned long flags; /**< Image flags */ 46 }; 47 segoff_t location; /**< 16-bit seg:off header location */ 48 union { 49 segoff_t segoff; /**< 16-bit seg:off entry point */ 50 unsigned long linear; /**< 32-bit entry point */ 51 } execaddr; 52} __attribute__ (( packed )); 53 54/** NBI magic number */ 55#define NBI_MAGIC 0x1B031336UL 56 57/* Interpretation of the "length" fields */ 58#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 ) 59#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 ) 60#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) ) 61 62/* Interpretation of the "flags" fields */ 63#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) ) 64#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) ) 65 66/** NBI header length */ 67#define NBI_HEADER_LENGTH 512 68 69/** 70 * An NBI segment header 71 * 72 * Note that the length field uses a peculiar encoding; use the 73 * NBI_LENGTH() macro to decode the actual header length. 74 * 75 */ 76struct segheader { 77 unsigned char length; /**< Nibble-coded header length */ 78 unsigned char vendortag; /**< Vendor-defined private tag */ 79 unsigned char reserved; 80 unsigned char flags; /**< Segment flags */ 81 unsigned long loadaddr; /**< Load address */ 82 unsigned long imglength; /**< Segment length in NBI file */ 83 unsigned long memlength; /**< Segment length in memory */ 84}; 85 86/* Interpretation of the "flags" fields */ 87#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 ) 88#define NBI_LOADADDR_ABS 0x00 89#define NBI_LOADADDR_AFTER 0x01 90#define NBI_LOADADDR_END 0x02 91#define NBI_LOADADDR_BEFORE 0x03 92#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) ) 93 94/* Define a type for passing info to a loaded program */ 95struct ebinfo { 96 uint8_t major, minor; /* Version */ 97 uint16_t flags; /* Bit flags */ 98}; 99 100/** Info passed to NBI image */ 101static struct ebinfo loaderinfo = { 102 VERSION_MAJOR, VERSION_MINOR, 103 0 104}; 105 106/** 107 * Prepare a segment for an NBI image 108 * 109 * @v image NBI image 110 * @v offset Offset within NBI image 111 * @v filesz Length of initialised-data portion of the segment 112 * @v memsz Total length of the segment 113 * @v src Source for initialised data 114 * @ret rc Return status code 115 */ 116static int nbi_prepare_segment ( struct image *image, size_t offset __unused, 117 userptr_t dest, size_t filesz, size_t memsz ){ 118 int rc; 119 120 if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) { 121 DBGC ( image, "NBI %p could not prepare segment: %s\n", 122 image, strerror ( rc ) ); 123 return rc; 124 } 125 126 return 0; 127} 128 129/** 130 * Load a segment for an NBI image 131 * 132 * @v image NBI image 133 * @v offset Offset within NBI image 134 * @v filesz Length of initialised-data portion of the segment 135 * @v memsz Total length of the segment 136 * @v src Source for initialised data 137 * @ret rc Return status code 138 */ 139static int nbi_load_segment ( struct image *image, size_t offset, 140 userptr_t dest, size_t filesz, 141 size_t memsz __unused ) { 142 memcpy_user ( dest, 0, image->data, offset, filesz ); 143 return 0; 144} 145 146/** 147 * Process segments of an NBI image 148 * 149 * @v image NBI image 150 * @v imgheader Image header information 151 * @v process Function to call for each segment 152 * @ret rc Return status code 153 */ 154static int nbi_process_segments ( struct image *image, 155 struct imgheader *imgheader, 156 int ( * process ) ( struct image *image, 157 size_t offset, 158 userptr_t dest, 159 size_t filesz, 160 size_t memsz ) ) { 161 struct segheader sh; 162 size_t offset = 0; 163 size_t sh_off; 164 userptr_t dest; 165 size_t filesz; 166 size_t memsz; 167 int rc; 168 169 /* Copy image header to target location */ 170 dest = real_to_user ( imgheader->location.segment, 171 imgheader->location.offset ); 172 filesz = memsz = NBI_HEADER_LENGTH; 173 if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 ) 174 return rc; 175 offset += filesz; 176 177 /* Process segments in turn */ 178 sh_off = NBI_LENGTH ( imgheader->length ); 179 do { 180 /* Read segment header */ 181 copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) ); 182 if ( sh.length == 0 ) { 183 /* Avoid infinite loop? */ 184 DBGC ( image, "NBI %p invalid segheader length 0\n", 185 image ); 186 return -ENOEXEC; 187 } 188 189 /* Calculate segment load address */ 190 switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) { 191 case NBI_LOADADDR_ABS: 192 dest = phys_to_user ( sh.loadaddr ); 193 break; 194 case NBI_LOADADDR_AFTER: 195 dest = userptr_add ( dest, memsz + sh.loadaddr ); 196 break; 197 case NBI_LOADADDR_BEFORE: 198 dest = userptr_add ( dest, -sh.loadaddr ); 199 break; 200 case NBI_LOADADDR_END: 201 /* Not correct according to the spec, but 202 * maintains backwards compatibility with 203 * previous versions of Etherboot. 204 */ 205 dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024 206 - sh.loadaddr ); 207 break; 208 default: 209 /* Cannot be reached */ 210 assert ( 0 ); 211 } 212 213 /* Process this segment */ 214 filesz = sh.imglength; 215 memsz = sh.memlength; 216 if ( ( offset + filesz ) > image->len ) { 217 DBGC ( image, "NBI %p segment outside file\n", image ); 218 return -ENOEXEC; 219 } 220 if ( ( rc = process ( image, offset, dest, 221 filesz, memsz ) ) != 0 ) { 222 return rc; 223 } 224 offset += filesz; 225 226 /* Next segheader */ 227 sh_off += NBI_LENGTH ( sh.length ); 228 if ( sh_off >= NBI_HEADER_LENGTH ) { 229 DBGC ( image, "NBI %p header overflow\n", image ); 230 return -ENOEXEC; 231 } 232 233 } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) ); 234 235 if ( offset != image->len ) { 236 DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n", 237 image, image->len, offset ); 238 return -ENOEXEC; 239 } 240 241 return 0; 242} 243 244/** 245 * Load an NBI image into memory 246 * 247 * @v image NBI image 248 * @ret rc Return status code 249 */ 250static int nbi_load ( struct image *image ) { 251 struct imgheader imgheader; 252 int rc; 253 254 /* If we don't have enough data give up */ 255 if ( image->len < NBI_HEADER_LENGTH ) { 256 DBGC ( image, "NBI %p too short for an NBI image\n", image ); 257 return -ENOEXEC; 258 } 259 260 /* Check image header */ 261 copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) ); 262 if ( imgheader.magic != NBI_MAGIC ) { 263 DBGC ( image, "NBI %p has no NBI signature\n", image ); 264 return -ENOEXEC; 265 } 266 267 /* This is an NBI image, valid or otherwise */ 268 if ( ! image->type ) 269 image->type = &nbi_image_type; 270 271 DBGC ( image, "NBI %p placing header at %hx:%hx\n", image, 272 imgheader.location.segment, imgheader.location.offset ); 273 274 /* NBI files can have overlaps between segments; the bss of 275 * one segment may overlap the initialised data of another. I 276 * assume this is a design flaw, but there are images out 277 * there that we need to work with. We therefore do two 278 * passes: first to initialise the segments, then to copy the 279 * data. This avoids zeroing out already-copied data. 280 */ 281 if ( ( rc = nbi_process_segments ( image, &imgheader, 282 nbi_prepare_segment ) ) != 0 ) 283 return rc; 284 if ( ( rc = nbi_process_segments ( image, &imgheader, 285 nbi_load_segment ) ) != 0 ) 286 return rc; 287 288 /* Record header address in image private data field */ 289 image->priv.user = real_to_user ( imgheader.location.segment, 290 imgheader.location.offset ); 291 292 return 0; 293} 294 295/** 296 * Boot a 16-bit NBI image 297 * 298 * @v imgheader Image header information 299 * @ret rc Return status code, if image returns 300 */ 301static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) { 302 int discard_D, discard_S, discard_b; 303 int rc; 304 305 DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, 306 imgheader->execaddr.segoff.segment, 307 imgheader->execaddr.segoff.offset ); 308 309 gateA20_unset(); 310 311 __asm__ __volatile__ ( 312 REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */ 313 "pushw %%bx\n\t" 314 "pushl %%esi\n\t" /* location */ 315 "pushw %%cs\n\t" /* lcall execaddr */ 316 "call 1f\n\t" 317 "jmp 2f\n\t" 318 "\n1:\n\t" 319 "pushl %%edi\n\t" 320 "lret\n\t" 321 "\n2:\n\t" 322 "addw $8,%%sp\n\t" /* clean up stack */ ) 323 : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), 324 "=b" ( discard_b ) 325 : "D" ( imgheader->execaddr.segoff ), 326 "S" ( imgheader->location ), 327 "b" ( __from_data16 ( basemem_packet ) ) 328 : "ecx", "edx", "ebp" ); 329 330 gateA20_set(); 331 332 return rc; 333} 334 335/** 336 * Boot a 32-bit NBI image 337 * 338 * @v imgheader Image header information 339 * @ret rc Return status code, if image returns 340 */ 341static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { 342 int discard_D, discard_S, discard_b; 343 int rc; 344 345 DBGC ( image, "NBI %p executing 32-bit image at %lx\n", 346 image, imgheader->execaddr.linear ); 347 348 /* no gateA20_unset for PM call */ 349 350 /* Jump to OS with flat physical addressing */ 351 __asm__ __volatile__ ( 352 PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */ 353 "pushl %%esi\n\t" /* imgheader */ 354 "pushl %%eax\n\t" /* loaderinfo */ 355 "call *%%edi\n\t" 356 "addl $12, %%esp\n\t" /* clean up stack */ ) 357 : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), 358 "=b" ( discard_b ) 359 : "D" ( imgheader->execaddr.linear ), 360 "S" ( ( imgheader->location.segment << 4 ) + 361 imgheader->location.offset ), 362 "b" ( virt_to_phys ( basemem_packet ) ), 363 "a" ( virt_to_phys ( &loaderinfo ) ) 364 : "ecx", "edx", "ebp", "memory" ); 365 366 return rc; 367} 368 369/** 370 * Prepare DHCP parameter block for NBI image 371 * 372 * @v image NBI image 373 * @ret rc Return status code 374 */ 375static int nbi_prepare_dhcp ( struct image *image ) { 376 struct net_device *boot_netdev; 377 int rc; 378 379 boot_netdev = last_opened_netdev(); 380 if ( ! boot_netdev ) { 381 DBGC ( image, "NBI %p could not identify a network device\n", 382 image ); 383 return -ENODEV; 384 } 385 386 if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet, 387 sizeof ( basemem_packet ) ) ) != 0 ) { 388 DBGC ( image, "NBI %p failed to build DHCP packet\n", image ); 389 return rc; 390 } 391 392 return 0; 393} 394 395/** 396 * Execute a loaded NBI image 397 * 398 * @v image NBI image 399 * @ret rc Return status code 400 */ 401static int nbi_exec ( struct image *image ) { 402 struct imgheader imgheader; 403 int may_return; 404 int rc; 405 406 copy_from_user ( &imgheader, image->priv.user, 0, 407 sizeof ( imgheader ) ); 408 409 /* Prepare DHCP option block */ 410 if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 ) 411 return rc; 412 413 /* Shut down now if NBI image will not return */ 414 may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); 415 if ( ! may_return ) 416 shutdown ( SHUTDOWN_BOOT ); 417 418 /* Execute NBI image */ 419 if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { 420 rc = nbi_boot32 ( image, &imgheader ); 421 } else { 422 rc = nbi_boot16 ( image, &imgheader ); 423 } 424 425 if ( ! may_return ) { 426 /* Cannot continue after shutdown() called */ 427 DBGC ( image, "NBI %p returned %d from non-returnable image\n", 428 image, rc ); 429 while ( 1 ) {} 430 } 431 432 DBGC ( image, "NBI %p returned %d\n", image, rc ); 433 434 return rc; 435} 436 437/** NBI image type */ 438struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = { 439 .name = "NBI", 440 .load = nbi_load, 441 .exec = nbi_exec, 442}; 443