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