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