source: bootcd/isolinux/syslinux-6.03/gpxe/src/arch/i386/prefix/pxeprefix.S

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

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