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