|
|
1.1 root 1: FILE_LICENCE ( GPL2_OR_LATER )
2:
3: #define PXENV_UNDI_SHUTDOWN 0x0005
4: #define PXENV_UNDI_GET_NIC_TYPE 0x0012
5: #define PXENV_UNDI_GET_IFACE_INFO 0x0013
6: #define PXENV_STOP_UNDI 0x0015
7: #define PXENV_UNLOAD_STACK 0x0070
8:
9: #define PXE_HACK_EB54 0x0001
10:
11: .text
12: .arch i386
13: .org 0
14: .code16
15:
16: #include <undi.h>
17:
18: #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
19: #define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
20: #define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
21:
22: /*****************************************************************************
23: * Entry point: set operating context, print welcome message
24: *****************************************************************************
25: */
26: .section ".prefix", "ax", @progbits
27: .globl _pxe_start
28: _pxe_start:
29: jmp $0x7c0, $1f
30: 1:
31: /* Preserve registers for possible return to PXE */
32: pushfl
33: pushal
34: pushw %gs
35: pushw %fs
36: pushw %es
37: pushw %ds
38:
39: /* Store magic word on PXE stack and remember PXE %ss:esp */
40: pushl $STACK_MAGIC
41: movw %ss, %cs:pxe_ss
42: movl %esp, %cs:pxe_esp
43:
44: /* Set up segments */
45: movw %cs, %ax
46: movw %ax, %ds
47: movw $0x40, %ax /* BIOS data segment access */
48: movw %ax, %fs
49: /* Set up stack just below 0x7c00 */
50: xorw %ax, %ax
51: movw %ax, %ss
52: movl $0x7c00, %esp
53: /* Clear direction flag, for the sake of sanity */
54: cld
55: /* Print welcome message */
56: movw $10f, %si
57: xorw %di, %di
58: call print_message
59: .section ".prefix.data", "aw", @progbits
60: 10: .asciz "PXE->EB:"
61: .previous
62:
63: /*****************************************************************************
64: * Find us a usable !PXE or PXENV+ entry point
65: *****************************************************************************
66: */
67: detect_pxe:
68: /* Plan A: !PXE pointer from the stack */
69: lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */
70: lesw %gs:52(%bp), %bx
71: call is_valid_ppxe
72: je have_ppxe
73:
74: /* Plan B: PXENV+ pointer from initial ES:BX */
75: movw %gs:32(%bp),%bx
76: movw %gs:8(%bp),%es
77: call is_valid_pxenv
78: je have_pxenv
79:
80: /* Plan C: PXENV+ structure via INT 1Ah */
81: movw $0x5650, %ax
82: int $0x1a
83: jc 1f
84: cmpw $0x564e, %ax
85: jne 1f
86: call is_valid_pxenv
87: je have_pxenv
88: 1:
89: /* Plan D: scan base memory for !PXE */
90: call memory_scan_ppxe
91: je have_ppxe
92:
93: /* Plan E: scan base memory for PXENV+ */
94: call memory_scan_pxenv
95: jne stack_not_found
96:
97: have_pxenv:
98: movw %bx, pxenv_offset
99: movw %es, pxenv_segment
100:
101: cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */
102: jb 1f
103: cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */
104: jb 2f
105:
106: lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */
107: call is_valid_ppxe
108: je have_ppxe
109: 2:
110: call memory_scan_ppxe /* We are *supposed* to have !PXE... */
111: je have_ppxe
112: 1:
113: lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */
114:
115: /* Record entry point and UNDI segments */
116: pushl %es:0x0a(%bx) /* Entry point */
117: pushw %es:0x24(%bx) /* UNDI code segment */
118: pushw %es:0x26(%bx) /* UNDI code size */
119: pushw %es:0x20(%bx) /* UNDI data segment */
120: pushw %es:0x22(%bx) /* UNDI data size */
121:
122: /* Print "PXENV+ at <address>" */
123: movw $10f, %si
124: jmp check_have_stack
125: .section ".prefix.data", "aw", @progbits
126: 10: .asciz " PXENV+ at "
127: .previous
128:
129: have_ppxe:
130: movw %bx, ppxe_offset
131: movw %es, ppxe_segment
132:
133: pushl %es:0x10(%bx) /* Entry point */
134: pushw %es:0x30(%bx) /* UNDI code segment */
135: pushw %es:0x36(%bx) /* UNDI code size */
136: pushw %es:0x28(%bx) /* UNDI data segment */
137: pushw %es:0x2e(%bx) /* UNDI data size */
138:
139: /* Print "!PXE at <address>" */
140: movw $10f, %si
141: jmp check_have_stack
142: .section ".prefix.data", "aw", @progbits
143: 10: .asciz " !PXE at "
144: .previous
145:
146: is_valid_ppxe:
147: cmpl $0x45585021, %es:(%bx)
148: jne 1f
149: movzbw %es:4(%bx), %cx
150: cmpw $0x58, %cx
151: jae is_valid_checksum
152: 1:
153: ret
154:
155: is_valid_pxenv:
156: cmpl $0x4e455850, %es:(%bx)
157: jne 1b
158: cmpw $0x2b56, %es:4(%bx)
159: jne 1b
160: movzbw %es:8(%bx), %cx
161: cmpw $0x28, %cx
162: jb 1b
163:
164: is_valid_checksum:
165: pushw %ax
166: movw %bx, %si
167: xorw %ax, %ax
168: 2:
169: es lodsb
170: addb %al, %ah
171: loopw 2b
172: popw %ax
173: ret
174:
175: memory_scan_ppxe:
176: movw $is_valid_ppxe, %dx
177: jmp memory_scan_common
178:
179: memory_scan_pxenv:
180: movw $is_valid_pxenv, %dx
181:
182: memory_scan_common:
183: movw %fs:(0x13), %ax
184: shlw $6, %ax
185: decw %ax
186: 1: incw %ax
187: cmpw $( 0xa000 - 1 ), %ax
188: ja 2f
189: movw %ax, %es
190: xorw %bx, %bx
191: call *%dx
192: jne 1b
193: 2: ret
194:
195: /*****************************************************************************
196: * Sanity check: we must have an entry point
197: *****************************************************************************
198: */
199: check_have_stack:
200: /* Save common values pushed onto the stack */
201: popl undi_data_segoff
202: popl undi_code_segoff
203: popl entry_segoff
204:
205: /* Print have !PXE/PXENV+ message; structure pointer in %es:%bx */
206: call print_message
207: call print_segoff
208: movb $( ',' ), %al
209: call print_character
210:
211: /* Check for entry point */
212: movl entry_segoff, %eax
213: testl %eax, %eax
214: jnz 99f
215: /* No entry point: print message and skip everything else */
216: stack_not_found:
217: movw $10f, %si
218: call print_message
219: jmp finished
220: .section ".prefix.data", "aw", @progbits
221: 10: .asciz " No PXE stack found!\n"
222: .previous
223: 99:
224:
225: /*****************************************************************************
226: * Calculate base memory usage by UNDI
227: *****************************************************************************
228: */
229: find_undi_basemem_usage:
230: movw undi_code_segment, %ax
231: movw undi_code_size, %bx
232: movw undi_data_segment, %cx
233: movw undi_data_size, %dx
234: cmpw %ax, %cx
235: ja 1f
236: xchgw %ax, %cx
237: xchgw %bx, %dx
238: 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
239: shrw $6, %ax /* Round down to nearest kB */
240: movw %ax, undi_fbms_start
241: addw $0x0f, %dx /* Round up to next segment */
242: shrw $4, %dx
243: addw %dx, %cx
244: addw $((1024 / 16) - 1), %cx /* Round up to next kB */
245: shrw $6, %cx
246: movw %cx, undi_fbms_end
247:
248: /*****************************************************************************
249: * Print information about detected PXE stack
250: *****************************************************************************
251: */
252: print_structure_information:
253: /* Print entry point */
254: movw $10f, %si
255: call print_message
256: les entry_segoff, %bx
257: call print_segoff
258: .section ".prefix.data", "aw", @progbits
259: 10: .asciz " entry point at "
260: .previous
261: /* Print UNDI code segment */
262: movw $10f, %si
263: call print_message
264: les undi_code_segoff, %bx
265: call print_segoff
266: .section ".prefix.data", "aw", @progbits
267: 10: .asciz "\n UNDI code segment "
268: .previous
269: /* Print UNDI data segment */
270: movw $10f, %si
271: call print_message
272: les undi_data_segoff, %bx
273: call print_segoff
274: .section ".prefix.data", "aw", @progbits
275: 10: .asciz ", data segment "
276: .previous
277: /* Print UNDI memory usage */
278: movw $10f, %si
279: call print_message
280: movw undi_fbms_start, %ax
281: call print_word
282: movb $( '-' ), %al
283: call print_character
284: movw undi_fbms_end, %ax
285: call print_word
286: movw $20f, %si
287: call print_message
288: .section ".prefix.data", "aw", @progbits
289: 10: .asciz " ("
290: 20: .asciz "kB)\n"
291: .previous
292:
293: /*****************************************************************************
294: * Determine physical device
295: *****************************************************************************
296: */
297: get_physical_device:
298: /* Issue PXENV_UNDI_GET_NIC_TYPE */
299: movw $PXENV_UNDI_GET_NIC_TYPE, %bx
300: call pxe_call
301: jnc 1f
302: call print_pxe_error
303: jmp no_physical_device
304: 1: /* Determine physical device type */
305: movb ( pxe_parameter_structure + 0x02 ), %al
306: cmpb $2, %al
307: je pci_physical_device
308: jmp no_physical_device
309:
310: pci_physical_device:
311: /* Record PCI bus:dev.fn and vendor/device IDs */
312: movl ( pxe_parameter_structure + 0x03 ), %eax
313: movl %eax, pci_vendor
314: movw ( pxe_parameter_structure + 0x0b ), %ax
315: movw %ax, pci_busdevfn
316: movw $10f, %si
317: call print_message
318: call print_pci_busdevfn
319: jmp 99f
320: .section ".prefix.data", "aw", @progbits
321: 10: .asciz " UNDI device is PCI "
322: .previous
323:
324: no_physical_device:
325: /* No device found, or device type not understood */
326: movw $10f, %si
327: call print_message
328: .section ".prefix.data", "aw", @progbits
329: 10: .asciz " Unable to determine UNDI physical device"
330: .previous
331:
332: 99:
333:
334: /*****************************************************************************
335: * Determine interface type
336: *****************************************************************************
337: */
338: get_iface_type:
339: /* Issue PXENV_UNDI_GET_IFACE_INFO */
340: movw $PXENV_UNDI_GET_IFACE_INFO, %bx
341: call pxe_call
342: jnc 1f
343: call print_pxe_error
344: jmp 99f
345: 1: /* Print interface type */
346: movw $10f, %si
347: call print_message
348: leaw ( pxe_parameter_structure + 0x02 ), %si
349: call print_message
350: .section ".prefix.data", "aw", @progbits
351: 10: .asciz ", type "
352: .previous
353: /* Check for "Etherboot" interface type */
354: cmpl $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
355: jne 99f
356: cmpl $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
357: jne 99f
358: movw $10f, %si
359: call print_message
360: .section ".prefix.data", "aw", @progbits
361: 10: .asciz " (workaround enabled)"
362: .previous
363: /* Flag Etherboot workarounds as required */
364: orw $PXE_HACK_EB54, pxe_hacks
365:
366: 99: movb $0x0a, %al
367: call print_character
368:
369: /*****************************************************************************
370: * Leave NIC in a safe state
371: *****************************************************************************
372: */
373: #ifndef PXELOADER_KEEP_PXE
374: shutdown_nic:
375: /* Issue PXENV_UNDI_SHUTDOWN */
376: movw $PXENV_UNDI_SHUTDOWN, %bx
377: call pxe_call
378: jnc 1f
379: call print_pxe_error
380: 1:
381: unload_base_code:
382: /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
383: * we must not issue this call if the underlying stack is
384: * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
385: */
386: #ifdef PXELOADER_KEEP_UNDI
387: testw $PXE_HACK_EB54, pxe_hacks
388: jnz 99f
389: #endif /* PXELOADER_KEEP_UNDI */
390: /* Issue PXENV_UNLOAD_STACK */
391: movw $PXENV_UNLOAD_STACK, %bx
392: call pxe_call
393: jnc 1f
394: call print_pxe_error
395: jmp 99f
396: 1: /* Free base memory used by PXE base code */
397: movw undi_fbms_start, %ax
398: movw %fs:(0x13), %bx
399: call free_basemem
400: 99:
401: andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
402: #endif /* PXELOADER_KEEP_PXE */
403:
404: /*****************************************************************************
405: * Unload UNDI driver
406: *****************************************************************************
407: */
408: #ifndef PXELOADER_KEEP_UNDI
409: unload_undi:
410: /* Issue PXENV_STOP_UNDI */
411: movw $PXENV_STOP_UNDI, %bx
412: call pxe_call
413: jnc 1f
414: call print_pxe_error
415: jmp 99f
416: 1: /* Free base memory used by UNDI */
417: movw undi_fbms_end, %ax
418: movw undi_fbms_start, %bx
419: call free_basemem
420: /* Clear UNDI_FL_STARTED */
421: andw $~UNDI_FL_STARTED, flags
422: 99:
423: #endif /* PXELOADER_KEEP_UNDI */
424:
425: /*****************************************************************************
426: * Print remaining free base memory
427: *****************************************************************************
428: */
429: print_free_basemem:
430: movw $10f, %si
431: call print_message
432: movw %fs:(0x13), %ax
433: call print_word
434: movw $20f, %si
435: call print_message
436: .section ".prefix.data", "aw", @progbits
437: 10: .asciz " "
438: 20: .asciz "kB free base memory after PXE unload\n"
439: .previous
440:
441: /*****************************************************************************
442: * Exit point
443: *****************************************************************************
444: */
445: finished:
446: jmp run_ipxe
447:
448: /*****************************************************************************
449: * Subroutine: print segment:offset address
450: *
451: * Parameters:
452: * %es:%bx : segment:offset address to print
453: * %ds:di : output buffer (or %di=0 to print to console)
454: * Returns:
455: * %ds:di : next character in output buffer (if applicable)
456: *****************************************************************************
457: */
458: print_segoff:
459: /* Preserve registers */
460: pushw %ax
461: /* Print "<segment>:offset" */
462: movw %es, %ax
463: call print_hex_word
464: movb $( ':' ), %al
465: call print_character
466: movw %bx, %ax
467: call print_hex_word
468: /* Restore registers and return */
469: popw %ax
470: ret
471:
472: /*****************************************************************************
473: * Subroutine: print decimal word
474: *
475: * Parameters:
476: * %ax : word to print
477: * %ds:di : output buffer (or %di=0 to print to console)
478: * Returns:
479: * %ds:di : next character in output buffer (if applicable)
480: *****************************************************************************
481: */
482: print_word:
483: /* Preserve registers */
484: pushw %ax
485: pushw %bx
486: pushw %cx
487: pushw %dx
488: /* Build up digit sequence on stack */
489: movw $10, %bx
490: xorw %cx, %cx
491: 1: xorw %dx, %dx
492: divw %bx, %ax
493: pushw %dx
494: incw %cx
495: testw %ax, %ax
496: jnz 1b
497: /* Print digit sequence */
498: 1: popw %ax
499: call print_hex_nibble
500: loop 1b
501: /* Restore registers and return */
502: popw %dx
503: popw %cx
504: popw %bx
505: popw %ax
506: ret
507:
508: /*****************************************************************************
509: * Subroutine: zero 1kB block of base memory
510: *
511: * Parameters:
512: * %bx : block to zero (in kB)
513: * Returns:
514: * Nothing
515: *****************************************************************************
516: */
517: zero_kb:
518: /* Preserve registers */
519: pushw %ax
520: pushw %cx
521: pushw %di
522: pushw %es
523: /* Zero block */
524: movw %bx, %ax
525: shlw $6, %ax
526: movw %ax, %es
527: movw $0x400, %cx
528: xorw %di, %di
529: xorw %ax, %ax
530: rep stosb
531: /* Restore registers and return */
532: popw %es
533: popw %di
534: popw %cx
535: popw %ax
536: ret
537:
538: /*****************************************************************************
539: * Subroutine: free and zero base memory
540: *
541: * Parameters:
542: * %ax : Desired new free base memory counter (in kB)
543: * %bx : Expected current free base memory counter (in kB)
544: * %fs : BIOS data segment (0x40)
545: * Returns:
546: * None
547: *
548: * The base memory from %bx kB to %ax kB is unconditionally zeroed.
549: * It will be freed if and only if the expected current free base
550: * memory counter (%bx) matches the actual current free base memory
551: * counter in 0x40:0x13; if this does not match then the memory will
552: * be leaked.
553: *****************************************************************************
554: */
555: free_basemem:
556: /* Zero base memory */
557: pushw %bx
558: 1: cmpw %bx, %ax
559: je 2f
560: call zero_kb
561: incw %bx
562: jmp 1b
563: 2: popw %bx
564: /* Free base memory */
565: cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */
566: jne 1f /* is correct */
567: 1: movw %ax, %fs:(0x13)
568: ret
569:
570: /*****************************************************************************
571: * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
572: *
573: * Parameters:
574: * %bx : PXE API call number
575: * %ds:pxe_parameter_structure : Parameters for PXE API call
576: * Returns:
577: * %ax : PXE status code (not exit code)
578: * CF set if %ax is non-zero
579: *****************************************************************************
580: */
581: pxe_call:
582: /* Preserve registers */
583: pushw %di
584: pushw %es
585: /* Set up registers for PXENV+ API. %bx already set up */
586: pushw %ds
587: popw %es
588: movw $pxe_parameter_structure, %di
589: /* Set up stack for !PXE API */
590: pushw %es
591: pushw %di
592: pushw %bx
593: /* Make the API call */
594: lcall *entry_segoff
595: /* Reset the stack */
596: addw $6, %sp
597: movw pxe_parameter_structure, %ax
598: clc
599: testw %ax, %ax
600: jz 1f
601: stc
602: 1: /* Clear direction flag, for the sake of sanity */
603: cld
604: /* Restore registers and return */
605: popw %es
606: popw %di
607: ret
608:
609: /*****************************************************************************
610: * Subroutine: print PXE API call error message
611: *
612: * Parameters:
613: * %ax : PXE status code
614: * %bx : PXE API call number
615: * Returns:
616: * Nothing
617: *****************************************************************************
618: */
619: print_pxe_error:
620: pushw %si
621: movw $10f, %si
622: call print_message
623: xchgw %ax, %bx
624: call print_hex_word
625: movw $20f, %si
626: call print_message
627: xchgw %ax, %bx
628: call print_hex_word
629: movw $30f, %si
630: call print_message
631: popw %si
632: ret
633: .section ".prefix.data", "aw", @progbits
634: 10: .asciz " UNDI API call "
635: 20: .asciz " failed: status code "
636: 30: .asciz "\n"
637: .previous
638:
639: /*****************************************************************************
640: * PXE data structures
641: *****************************************************************************
642: */
643: .section ".prefix.data"
644:
645: pxe_esp: .long 0
646: pxe_ss: .word 0
647:
648: pxe_parameter_structure: .fill 64
649:
650: undi_code_segoff:
651: undi_code_size: .word 0
652: undi_code_segment: .word 0
653:
654: undi_data_segoff:
655: undi_data_size: .word 0
656: undi_data_segment: .word 0
657:
658: pxe_hacks: .word 0
659:
660: /* The following fields are part of a struct undi_device */
661:
662: undi_device:
663:
664: pxenv_segoff:
665: pxenv_offset: .word 0
666: pxenv_segment: .word 0
667:
668: ppxe_segoff:
669: ppxe_offset: .word 0
670: ppxe_segment: .word 0
671:
672: entry_segoff:
673: entry_offset: .word 0
674: entry_segment: .word 0
675:
676: undi_fbms_start: .word 0
677: undi_fbms_end: .word 0
678:
679: pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
680: isapnp_csn: .word UNDI_NO_ISAPNP_CSN
681: isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
682:
683: pci_vendor: .word 0
684: pci_device: .word 0
685: flags:
686: .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
687:
688: .equ undi_device_size, ( . - undi_device )
689:
690: /*****************************************************************************
691: * Run iPXE main code
692: *****************************************************************************
693: */
694: .section ".prefix"
695: run_ipxe:
696: /* Install iPXE */
697: call install
698:
699: /* Set up real-mode stack */
700: movw %bx, %ss
701: movw $_estack16, %sp
702:
703: #ifdef PXELOADER_KEEP_UNDI
704: /* Copy our undi_device structure to the preloaded_undi variable */
705: movw %bx, %es
706: movw $preloaded_undi, %di
707: movw $undi_device, %si
708: movw $undi_device_size, %cx
709: rep movsb
710: #endif
711:
712: /* Retrieve PXE %ss:esp */
713: movw pxe_ss, %di
714: movl pxe_esp, %ebp
715:
716: /* Jump to .text16 segment with %ds pointing to .data16 */
717: movw %bx, %ds
718: pushw %ax
719: pushw $1f
720: lret
721: .section ".text16", "ax", @progbits
722: 1:
723: /* Update the exit hook */
724: movw %cs,pxe_exit_hook+2
725: push %ax
726: mov $2f,%ax
727: mov %ax,pxe_exit_hook
728: pop %ax
729:
730: /* Run main program */
731: pushl $main
732: pushw %cs
733: call prot_call
734: popl %ecx /* discard */
735:
736: /* Uninstall iPXE */
737: call uninstall
738:
739: /* Restore PXE stack */
740: movw %di, %ss
741: movl %ebp, %esp
742:
743: /* Jump to hook if applicable */
744: ljmpw *pxe_exit_hook
745:
746: 2: /* Check PXE stack magic */
747: popl %eax
748: cmpl $STACK_MAGIC, %eax
749: jne 1f
750:
751: /* PXE stack OK: return to caller */
752: popw %ds
753: popw %es
754: popw %fs
755: popw %gs
756: popal
757: popfl
758: xorw %ax, %ax /* Return success */
759: lret
760:
761: 1: /* PXE stack corrupt or removed: use INT 18 */
762: int $0x18
763: .previous
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.