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