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