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