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