|
|
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.