[pxeprefix] Work around bug in Etherboot 5.4 when loading undionly.kpxe
[people/sha0/gpxe.git] / src / arch / i386 / prefix / pxeprefix.S
index 82b1da1..c6ae12c 100644 (file)
@@ -1,8 +1,11 @@
 #define PXENV_UNDI_SHUTDOWN            0x0005
 #define        PXENV_UNDI_GET_NIC_TYPE         0x0012
+#define PXENV_UNDI_GET_IFACE_INFO      0x0013
 #define        PXENV_STOP_UNDI                 0x0015
 #define PXENV_UNLOAD_STACK             0x0070
 
+#define PXE_HACK_EB54                  0x0001
+
        .text
        .arch i386
        .org 0
@@ -11,6 +14,8 @@
 #include <undi.h>
 
 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
+#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
+#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
 
 /*****************************************************************************
  * Entry point:        set operating context, print welcome message
@@ -307,8 +312,6 @@ pci_physical_device:
        movw    $10f, %si
        call    print_message
        call    print_pci_busdevfn
-       movb    $0x0a, %al
-       call    print_character
        jmp     99f
        .section ".prefix.data", "aw", @progbits
 10:    .asciz  "         UNDI device is PCI "
@@ -319,11 +322,46 @@ no_physical_device:
        movw    $10f, %si
        call    print_message
        .section ".prefix.data", "aw", @progbits
-10:    .asciz  "         Unable to determine UNDI physical device\n"
+10:    .asciz  "         Unable to determine UNDI physical device"
        .previous
 
 99:
 
+/*****************************************************************************
+ * Determine interface type
+ *****************************************************************************
+ */
+get_iface_type:
+       /* Issue PXENV_UNDI_GET_IFACE_INFO */
+       movw    $PXENV_UNDI_GET_IFACE_INFO, %bx
+       call    pxe_call
+       jnc     1f
+       call    print_pxe_error
+       jmp     99f
+1:     /* Print interface type */
+       movw    $10f, %si
+       call    print_message
+       leaw    ( pxe_parameter_structure + 0x02 ), %si
+       call    print_message
+       .section ".prefix.data", "aw", @progbits
+10:    .asciz  ", type "
+       .previous
+       /* Check for "Etherboot" interface type */
+       cmpl    $EB_MAGIC_1, ( pxe_parameter_structure + 0x02 )
+       jne     99f
+       cmpl    $EB_MAGIC_2, ( pxe_parameter_structure + 0x06 )
+       jne     99f
+       movw    $10f, %si
+       call    print_message
+       .section ".prefix.data", "aw", @progbits
+10:    .asciz  " (workaround enabled)"
+       .previous
+       /* Flag Etherboot workarounds as required */
+       orw     $PXE_HACK_EB54, pxe_hacks
+
+99:    movb    $0x0a, %al
+       call    print_character
+
 /*****************************************************************************
  * Leave NIC in a safe state
  *****************************************************************************
@@ -337,6 +375,14 @@ shutdown_nic:
        call    print_pxe_error
 1:
 unload_base_code:
+       /* Etherboot treats PXENV_UNLOAD_STACK as PXENV_STOP_UNDI, so
+        * we must not issue this call if the underlying stack is
+        * Etherboot and we were not intending to issue a PXENV_STOP_UNDI.
+        */
+#ifdef PXELOADER_KEEP_UNDI
+       testw   $PXE_HACK_EB54, pxe_hacks
+       jnz     99f
+#endif /* PXELOADER_KEEP_UNDI */
        /* Issue PXENV_UNLOAD_STACK */
        movw    $PXENV_UNLOAD_STACK, %bx
        call    pxe_call
@@ -549,7 +595,9 @@ pxe_call:
        testw   %ax, %ax
        jz      1f
        stc
-1:     /* Restore registers and return */
+1:     /* Clear direction flag, for the sake of sanity */
+       cld
+       /* Restore registers and return */
        popw    %es
        popw    %di
        ret
@@ -593,7 +641,7 @@ print_pxe_error:
 pxe_esp:               .long 0
 pxe_ss:                        .word 0
 
-pxe_parameter_structure: .fill 20
+pxe_parameter_structure: .fill 64
 
 undi_code_segoff:
 undi_code_size:                .word 0
@@ -603,6 +651,8 @@ undi_data_segoff:
 undi_data_size:                .word 0
 undi_data_segment:     .word 0
 
+pxe_hacks:             .word 0
+
 /* The following fields are part of a struct undi_device */
 
 undi_device: